diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index de7c553f9..000000000 --- a/.eslintignore +++ /dev/null @@ -1,10 +0,0 @@ -**/lib/* -!.vuepress -docs -coverage -node_modules -.vuepress/enhanceApp.js -*.d.ts -dist -package-lock.json -cookieconsent.js \ No newline at end of file diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index a0e8a5974..000000000 --- a/.eslintrc.json +++ /dev/null @@ -1,71 +0,0 @@ -{ - "env":{ - "es2021":true, - "node":true, - "jest": true - }, - "extends":[ - "standard", - "prettier", - "plugin:prettier/recommended", - "plugin:vitest/recommended", - "plugin:import-esm/recommended" - ], - "overrides": [{ - "files":["*.ts"], - "rules": { - "@typescript-eslint/prefer-nullish-coalescing": "error" - } - } - - ], - "parser":"@typescript-eslint/parser", - "parserOptions":{ - "ecmaVersion":"latest", - "sourceType":"module", - "project": "tsconfig.json" - }, - "plugins":[ - "import-esm", - "vitest", - "simple-import-sort", - "import", - "json", - "@typescript-eslint" - ], - "ignorePatterns":["**/dist","website/doc/public/**"], - "rules":{ - "simple-import-sort/imports":"error", - "simple-import-sort/exports":"error", - "import/first":"error", - "import/newline-after-import":"error", - "import/no-duplicates":"error", - "sort-imports":"off", - "import/order":"off", - "no-console":"error", - "camelcase":"error", - "no-var":"error", - "space-before-function-paren":"off", - "dot-notation":"off", - "eol-last":"off", - "@typescript-eslint/no-var-requires":"warn", - "@typescript-eslint/no-this-alias":"warn", - "@typescript-eslint/explicit-module-boundary-types":"off", - "@typescript-eslint/consistent-type-imports":"error", - "no-unused-vars":"off", - "vitest/expect-expect":"off", - "curly":[ - "error", - "all" - ], - "brace-style":"error", - "@typescript-eslint/no-unused-vars":[ - "error", - { - "caughtErrorsIgnorePattern":"^_", - "argsIgnorePattern":"^_", - "varsIgnorePattern":"^_" - } - ] - } -} \ No newline at end of file diff --git a/.github/workflows/release_version.yml b/.github/workflows/release_version.yml index 523954e3d..8649e60a0 100644 --- a/.github/workflows/release_version.yml +++ b/.github/workflows/release_version.yml @@ -56,7 +56,7 @@ jobs: git config --global user.email 'sebastianwessel@users.noreply.github.com' git config --global push.followTags true ./scripts/commitVersion.sh - echo New version: + npm run build new_version=$(node -p -e "require('./package.json').version") new_branch=purista_v$new_version git checkout -b $new_branch diff --git a/.gitignore b/.gitignore index e60c939cd..0fcc5b289 100644 --- a/.gitignore +++ b/.gitignore @@ -29,4 +29,6 @@ lab __snapshots__ .tshy-build -.tshy \ No newline at end of file +.tshy + +gcloud-credentials.json \ No newline at end of file diff --git a/.nvmrc b/.nvmrc index 07533ba8b..8cfab175c 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -v20.11 +v20.17 diff --git a/.prettierrc b/.prettierrc deleted file mode 100644 index c9cb3989c..000000000 --- a/.prettierrc +++ /dev/null @@ -1,6 +0,0 @@ -{ - "singleQuote": true, - "trailingComma": "all", - "printWidth": 120, - "semi": false -} diff --git a/.vscode/launch.json b/.vscode/launch.json index 71e2edae5..2b551feee 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,32 +1,31 @@ { - "version": "0.2.0", - "configurations": [ + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Jest: current file", + "program": "${workspaceFolder}/node_modules/.bin/jest", + "args": ["${fileBasenameNoExtension}", "--config", "${workspaceFolder}/jest.config.js"], + "console": "integratedTerminal" + }, - { - "type": "node", - "request": "launch", - "name": "Jest: current file", - //"env": { "NODE_ENV": "test" }, - "program": "${workspaceFolder}/node_modules/.bin/jest", - "args": ["${fileBasenameNoExtension}", "--config", "${workspaceFolder}/jest.config.js"], - "console": "integratedTerminal" - }, - - { - "type": "node", - "request": "launch", - "name": "Jest debug current file", - "program": "${workspaceFolder}/node_modules/jest-cli/bin/jest", - "args": [ - "${fileBasename}", - "--verbose", - "-i", - "--no-cache", - "--watchAll", - "--config", "${workspaceFolder}/jest.config.js" - ], - "console": "integratedTerminal", - "internalConsoleOptions": "neverOpen" - } - ] -} \ No newline at end of file + { + "type": "node", + "request": "launch", + "name": "Jest debug current file", + "program": "${workspaceFolder}/node_modules/jest-cli/bin/jest", + "args": [ + "${fileBasename}", + "--verbose", + "-i", + "--no-cache", + "--watchAll", + "--config", + "${workspaceFolder}/jest.config.js" + ], + "console": "integratedTerminal", + "internalConsoleOptions": "neverOpen" + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json index 92c8beb61..f3bd81ab7 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,19 @@ { - "typescript.tsdk": "node_modules/typescript/lib", - "javascript.preferences.importModuleSpecifierEnding":"js" -} \ No newline at end of file + "typescript.tsdk": "node_modules/typescript/lib", + "javascript.preferences.importModuleSpecifierEnding": "js", + "editor.formatOnSave": true, + "editor.defaultFormatter": "biomejs.biome", + "editor.codeActionsOnSave": { + "quickfix.biome": "explicit", + "source.organizeImports.biome": "explicit" + }, + "[javascript]": { + "editor.defaultFormatter": "biomejs.biome" + }, + "[typescript]": { + "editor.defaultFormatter": "biomejs.biome" + }, + "[json]": { + "editor.defaultFormatter": "biomejs.biome" + } +} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cacd95b2c..ef030956c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,7 +6,7 @@ If you have a question that needs an answer, create an issue, and label it as a You can also join our Discord channel for questions and help: [Discord Channel](https://discord.gg/9feaUm3H2v) Issues for bugs or feature requests -If you encounter any bugs in the code, or want to request a new feature or enhancement, please [create an issue to report it](https://github.com/sebastianwessel/purista/issues). +If you encounter any bugs in the code, or want to request a new feature or enhancement, please [create an issue to report it](https://github.com/puristajs/purista/issues). Kindly add a label to indicate what type of issue it is. # Contribute Code diff --git a/README.md b/README.md index 531af3f3d..8abc42945 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,6 @@ See: [Security](./SECURITY.md) Contributors are welcome! -[![Sebastian Wessel](https://contrib.rocks/image?repo=sebastianwessel/purista)](https://github.com/sebastianwessel/purista) +[![Sebastian Wessel](https://contrib.rocks/image?repo=sebastianwessel/purista)](https://github.com/puristajs/purista) [PURISTA handbook](website/doc/handbook/) diff --git a/biome.json b/biome.json new file mode 100644 index 000000000..883e2cae2 --- /dev/null +++ b/biome.json @@ -0,0 +1,112 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json", + "organizeImports": { + "enabled": true + }, + "files": { + "ignore": [ + "**/lib/**/*", + "cookieconsent.js", + "cookieconsent-config2.js", + "package-lock.json", + "**/dist/**/*", + "*.d.ts", + "**/coverage/**/*", + "docs/**/*", + "**/.tshy/**/*", + "**/.tshy-build/**/*", + "**/.vitepress/cache/**", + "**/doc/public/**/*", + "examples/fullexample/signoz/**", + "examples/fullexample/grafana/**", + "examples/fullexample/jaeger/**", + "examples/fullexample/teletrace/**", + "examples/fullexample/uptrace/**", + "examples/fullexample/zipkin/**" + ] + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "complexity": { + "useArrowFunction": { + "level": "off", + "fix": "none" + }, + "useLiteralKeys": { + "level": "warn", + "fix": "safe" + }, + "noForEach": { + "level": "warn" + }, + "noBannedTypes": { + "level": "warn", + "fix": "safe" + } + }, + "correctness": { + "noUnusedImports": "error" + }, + "suspicious": { + "noExplicitAny": { + "level": "off" + }, + "noConsole": "error" + }, + "performance": { + "noAccumulatingSpread": { + "level": "warn" + } + }, + "style": { + "noUnusedTemplateLiteral": { + "level": "warn", + "fix": "safe" + } + } + } + }, + "formatter": { + "enabled": true, + "formatWithErrors": false, + "attributePosition": "auto", + "indentStyle": "tab", + "indentWidth": 2, + "lineWidth": 120, + "lineEnding": "lf" + }, + "javascript": { + "formatter": { + "quoteStyle": "single", + "arrowParentheses": "asNeeded", + "bracketSameLine": false, + "bracketSpacing": true, + "jsxQuoteStyle": "double", + "quoteProperties": "asNeeded", + "semicolons": "asNeeded", + "trailingCommas": "all", + "lineWidth": 120, + "indentWidth": 2, + "indentStyle": "tab" + } + }, + "json": { + "formatter": { + "trailingCommas": "none" + } + }, + "overrides": [ + { + "include": ["packages/cli/**"], + "linter": { + "rules": { + "suspicious": { + "noConsole": "off" + } + } + } + } + ] +} diff --git a/bun.lockb b/bun.lockb new file mode 100755 index 000000000..88f11ba96 Binary files /dev/null and b/bun.lockb differ diff --git a/examples/dapr-example/deployment/customsecrets.json b/examples/dapr-example/deployment/customsecrets.json index 8a95fac5f..55880f024 100644 --- a/examples/dapr-example/deployment/customsecrets.json +++ b/examples/dapr-example/deployment/customsecrets.json @@ -1,4 +1,4 @@ { - "secret_key":"secret_value", - "emailProviderAuthToken":"auth_token_from_secret_store" -} \ No newline at end of file + "secret_key": "secret_value", + "emailProviderAuthToken": "auth_token_from_secret_store" +} diff --git a/examples/dapr-example/deployment/service-email/index.ts b/examples/dapr-example/deployment/service-email/index.ts index 939e4b86e..8c86c27e2 100644 --- a/examples/dapr-example/deployment/service-email/index.ts +++ b/examples/dapr-example/deployment/service-email/index.ts @@ -10,55 +10,55 @@ import pretty from 'pino-pretty' import { emailV1Service } from '../../src/service/email/v1/emailV1Service.js' const main = async () => { - // initialize the logging - const logLevel: LogLevelName = 'debug' - const stream = pretty({ - colorize: false, - }) - const logger = new DefaultLogger(pino({ level: logLevel }, stream)) + // initialize the logging + const logLevel: LogLevelName = 'debug' + const stream = pretty({ + colorize: false, + }) + const logger = new DefaultLogger(pino({ level: logLevel }, stream)) - logger.info('application starts') + logger.info('application starts') - const spanProcessor = new SimpleSpanProcessor( - new ZipkinExporter({ - url: 'http://localhost:9411/api/v2/spans', - }), - ) + const spanProcessor = new SimpleSpanProcessor( + new ZipkinExporter({ + url: 'http://localhost:9411/api/v2/spans', + }), + ) - const eventBridge = new DaprEventBridge({ - spanProcessor, - logger, - serve, - }) + const eventBridge = new DaprEventBridge({ + spanProcessor, + logger, + serve, + }) - const secretStore = new DaprSecretStore({ logger, secretStoreName: 'local-secret-store' }) - const stateStore = new DaprStateStore({ logger, stateStoreName: 'local-state-store' }) - const configStore = new DaprConfigStore({ - logger, - configStoreName: 'local-config-store', - enableSet: true, - }) + const secretStore = new DaprSecretStore({ logger, secretStoreName: 'local-secret-store' }) + const stateStore = new DaprStateStore({ logger, stateStoreName: 'local-state-store' }) + const configStore = new DaprConfigStore({ + logger, + configStoreName: 'local-config-store', + enableSet: true, + }) - const emailService = await emailV1Service.getInstance(eventBridge, { - spanProcessor, - logger, - secretStore, - stateStore, - configStore, - }) - await emailService.start() + const emailService = await emailV1Service.getInstance(eventBridge, { + spanProcessor, + logger, + secretStore, + stateStore, + configStore, + }) + await emailService.start() - await eventBridge.start() + await eventBridge.start() - gracefulShutdown(logger, [ - // begin with the event bridge to no longer accept incoming messages - eventBridge, - emailService, - { - name: 'OTSpanProcessor', - destroy: () => spanProcessor.shutdown(), - }, - ]) + gracefulShutdown(logger, [ + // begin with the event bridge to no longer accept incoming messages + eventBridge, + emailService, + { + name: 'OTSpanProcessor', + destroy: () => spanProcessor.shutdown(), + }, + ]) } main() diff --git a/examples/dapr-example/deployment/service-user/index.ts b/examples/dapr-example/deployment/service-user/index.ts index b51ca3951..209917ff4 100644 --- a/examples/dapr-example/deployment/service-user/index.ts +++ b/examples/dapr-example/deployment/service-user/index.ts @@ -10,51 +10,51 @@ import pretty from 'pino-pretty' import { userV1Service } from '../../src/service/user/v1/userV1Service.js' const main = async () => { - // initialize the logging - const logLevel: LogLevelName = 'debug' - const stream = pretty({ - colorize: false, - }) - const logger = new DefaultLogger(pino({ level: logLevel }, stream)) - - logger.info('application starts') - - const spanProcessor = new SimpleSpanProcessor( - new ZipkinExporter({ - url: 'http://localhost:9411/api/v2/spans', - }), - ) - - const eventBridge = new DaprEventBridge({ - spanProcessor, - logger, - serve, - }) - - const secretStore = new DaprSecretStore({ logger, secretStoreName: 'local-secret-store' }) - const stateStore = new DaprStateStore({ logger, stateStoreName: 'local-state-store' }) - const configStore = new DaprConfigStore({ logger, configStoreName: 'local-config-store' }) - - const userService = await userV1Service.getInstance(eventBridge, { - spanProcessor, - logger, - secretStore, - stateStore, - configStore, - }) - await userService.start() - - await eventBridge.start() - - gracefulShutdown(logger, [ - // begin with the event bridge to no longer accept incoming messages - eventBridge, - userService, - { - name: 'OTSpanProcessor', - destroy: () => spanProcessor.shutdown(), - }, - ]) + // initialize the logging + const logLevel: LogLevelName = 'debug' + const stream = pretty({ + colorize: false, + }) + const logger = new DefaultLogger(pino({ level: logLevel }, stream)) + + logger.info('application starts') + + const spanProcessor = new SimpleSpanProcessor( + new ZipkinExporter({ + url: 'http://localhost:9411/api/v2/spans', + }), + ) + + const eventBridge = new DaprEventBridge({ + spanProcessor, + logger, + serve, + }) + + const secretStore = new DaprSecretStore({ logger, secretStoreName: 'local-secret-store' }) + const stateStore = new DaprStateStore({ logger, stateStoreName: 'local-state-store' }) + const configStore = new DaprConfigStore({ logger, configStoreName: 'local-config-store' }) + + const userService = await userV1Service.getInstance(eventBridge, { + spanProcessor, + logger, + secretStore, + stateStore, + configStore, + }) + await userService.start() + + await eventBridge.start() + + gracefulShutdown(logger, [ + // begin with the event bridge to no longer accept incoming messages + eventBridge, + userService, + { + name: 'OTSpanProcessor', + destroy: () => spanProcessor.shutdown(), + }, + ]) } main() diff --git a/examples/dapr-example/doc/ping.json b/examples/dapr-example/doc/ping.json index 740dfb4f0..d32b74cad 100644 --- a/examples/dapr-example/doc/ping.json +++ b/examples/dapr-example/doc/ping.json @@ -1,25 +1,25 @@ { - "id": "123-abc", - "timestamp": 1680274981573, - "messageType": "command", - "correlationId": "123", - "traceId": "123", - "instanceId": "123", - "principalId": "mocked-principal-id", - "contentType": "application/json", - "contentEncoding": "utf-8", - "sender": { - "serviceName": "", - "serviceVersion": "1", - "serviceTarget": "" - }, - "receiver": { - "serviceName": "User", - "serviceVersion": "1", - "serviceTarget": "ping" - }, - "payload": { - "payload": "PING", - "parameter": {} - } - } \ No newline at end of file + "id": "123-abc", + "timestamp": 1680274981573, + "messageType": "command", + "correlationId": "123", + "traceId": "123", + "instanceId": "123", + "principalId": "mocked-principal-id", + "contentType": "application/json", + "contentEncoding": "utf-8", + "sender": { + "serviceName": "", + "serviceVersion": "1", + "serviceTarget": "" + }, + "receiver": { + "serviceName": "User", + "serviceVersion": "1", + "serviceTarget": "ping" + }, + "payload": { + "payload": "PING", + "parameter": {} + } +} diff --git a/examples/dapr-example/package.json b/examples/dapr-example/package.json index 660d29860..112396104 100644 --- a/examples/dapr-example/package.json +++ b/examples/dapr-example/package.json @@ -1,39 +1,39 @@ { - "name": "@purista/dapr-example", - "version": "1.11.0", - "description": "example how to use the dapr package", - "homepage": "https://purista.dev", - "private": true, - "repository": { - "type": "git", - "url": "git@github.com:sebastianwessel/purista.git" - }, - "author": "Sebastian Wessel", - "license": "ISC", - "main": "src/index.ts", - "type": "module", - "engines": { - "node": ">=18.15" - }, - "scripts": { - "start": "dapr run -f ./deployment", - "lint": "eslint . --ext .ts,.json --cache . --fix", - "test": "vitest" - }, - "devDependencies": { - "@types/node": "^20.11.17", - "pino-pretty": "^10.3.1", - "sinon": "^17.0.1", - "tsx": "^4.7.0", - "typescript": "^5.3.3", - "vitest": "^1.3.0" - }, - "dependencies": { - "@hono/node-server": "^1.8.0", - "@opentelemetry/exporter-zipkin": "^1.19.0", - "@opentelemetry/sdk-trace-node": "^1.19.0", - "@purista/core": "*", - "@purista/dapr-sdk": "*", - "zod": "^3.22.4" - } + "name": "@purista/dapr-example", + "version": "1.11.0", + "description": "example how to use the dapr package", + "homepage": "https://purista.dev", + "private": true, + "repository": { + "type": "git", + "url": "git@github.com:puristajs/purista.git" + }, + "author": "Sebastian Wessel", + "license": "ISC", + "main": "src/index.ts", + "type": "module", + "engines": { + "node": ">=18.15" + }, + "scripts": { + "start": "dapr run -f ./deployment", + "lint": "npx @biomejs/biome check --write", + "test": "vitest" + }, + "devDependencies": { + "@types/node": "^22.5.1", + "pino-pretty": "^13.0.0", + "sinon": "^19.0.2", + "tsx": "^4.19.0", + "typescript": "^5.5.4", + "vitest": "^3.0.4" + }, + "dependencies": { + "@hono/node-server": "^1.12.2", + "@opentelemetry/exporter-zipkin": "^1.25.1", + "@opentelemetry/sdk-trace-node": "^1.26.0", + "@purista/core": "*", + "@purista/dapr-sdk": "*", + "zod": "^3.24.1" + } } diff --git a/examples/dapr-example/postman/dapr_local.postman_environment.json b/examples/dapr-example/postman/dapr_local.postman_environment.json index 2fffae950..0c5e266d5 100644 --- a/examples/dapr-example/postman/dapr_local.postman_environment.json +++ b/examples/dapr-example/postman/dapr_local.postman_environment.json @@ -57,4 +57,4 @@ "_postman_variable_scope": "environment", "_postman_exported_at": "2023-05-12T10:09:18.349Z", "_postman_exported_using": "Postman/10.13.5" -} \ No newline at end of file +} diff --git a/examples/dapr-example/postman/dapr_purista_example.postman_collection.json b/examples/dapr-example/postman/dapr_purista_example.postman_collection.json index 801c6b9b9..ecf024240 100644 --- a/examples/dapr-example/postman/dapr_purista_example.postman_collection.json +++ b/examples/dapr-example/postman/dapr_purista_example.postman_collection.json @@ -20,14 +20,9 @@ "url": { "raw": "http://{{dapr_host}}:{{dapr_port_email}}/v1.0/metadata", "protocol": "http", - "host": [ - "{{dapr_host}}" - ], + "host": ["{{dapr_host}}"], "port": "{{dapr_port_email}}", - "path": [ - "v1.0", - "metadata" - ] + "path": ["v1.0", "metadata"] } }, "response": [] @@ -40,15 +35,9 @@ "url": { "raw": "http://{{dapr_host}}:{{dapr_port_email}}/v1.0-alpha1/configuration/local-config-store", "protocol": "http", - "host": [ - "{{dapr_host}}" - ], + "host": ["{{dapr_host}}"], "port": "{{dapr_port_email}}", - "path": [ - "v1.0-alpha1", - "configuration", - "local-config-store" - ] + "path": ["v1.0-alpha1", "configuration", "local-config-store"] } }, "response": [] @@ -61,16 +50,9 @@ "url": { "raw": "http://{{dapr_host}}:{{dapr_port_email}}/v1.0/state/local-state-store/bulk", "protocol": "http", - "host": [ - "{{dapr_host}}" - ], + "host": ["{{dapr_host}}"], "port": "{{dapr_port_email}}", - "path": [ - "v1.0", - "state", - "local-state-store", - "bulk" - ] + "path": ["v1.0", "state", "local-state-store", "bulk"] } }, "response": [] @@ -83,16 +65,9 @@ "url": { "raw": "http://{{dapr_host}}:{{dapr_port_email}}/v1.0/secrets/local-secret-store/bulk", "protocol": "http", - "host": [ - "{{dapr_host}}" - ], + "host": ["{{dapr_host}}"], "port": "{{dapr_port_email}}", - "path": [ - "v1.0", - "secrets", - "local-secret-store", - "bulk" - ] + "path": ["v1.0", "secrets", "local-secret-store", "bulk"] } }, "response": [] @@ -120,16 +95,9 @@ "url": { "raw": "http://{{dapr_host}}:{{dapr_port_email}}/v1.0/publish/{{pubsub_name}}/{{eventname}}", "protocol": "http", - "host": [ - "{{dapr_host}}" - ], + "host": ["{{dapr_host}}"], "port": "{{dapr_port_email}}", - "path": [ - "v1.0", - "publish", - "{{pubsub_name}}", - "{{eventname}}" - ] + "path": ["v1.0", "publish", "{{pubsub_name}}", "{{eventname}}"] } }, "response": [] @@ -151,19 +119,9 @@ "url": { "raw": "http://{{dapr_host}}:{{dapr_port_email}}/v1.0/invoke/email-v1/method/purista/command/ping", "protocol": "http", - "host": [ - "{{dapr_host}}" - ], + "host": ["{{dapr_host}}"], "port": "{{dapr_port_email}}", - "path": [ - "v1.0", - "invoke", - "email-v1", - "method", - "purista", - "command", - "ping" - ] + "path": ["v1.0", "invoke", "email-v1", "method", "purista", "command", "ping"] } }, "response": [] @@ -190,15 +148,9 @@ "url": { "raw": "http://{{app_host}}:{{app_port_email}}/purista/command/ping", "protocol": "http", - "host": [ - "{{app_host}}" - ], + "host": ["{{app_host}}"], "port": "{{app_port_email}}", - "path": [ - "purista", - "command", - "ping" - ] + "path": ["purista", "command", "ping"] } }, "response": [] @@ -223,14 +175,9 @@ "url": { "raw": "http://{{app_host}}:{{app_port_email}}/dapr/subscribe", "protocol": "http", - "host": [ - "{{app_host}}" - ], + "host": ["{{app_host}}"], "port": "{{app_port_email}}", - "path": [ - "dapr", - "subscribe" - ] + "path": ["dapr", "subscribe"] } }, "response": [] @@ -253,14 +200,9 @@ "url": { "raw": "http://{{dapr_host}}:{{dapr_port_user}}/v1.0/metadata", "protocol": "http", - "host": [ - "{{dapr_host}}" - ], + "host": ["{{dapr_host}}"], "port": "{{dapr_port_user}}", - "path": [ - "v1.0", - "metadata" - ] + "path": ["v1.0", "metadata"] } }, "response": [] @@ -279,15 +221,9 @@ "url": { "raw": "http://{{dapr_host}}:{{dapr_port_user}}/v1.0-alpha1/configuration/local-config-store", "protocol": "http", - "host": [ - "{{dapr_host}}" - ], + "host": ["{{dapr_host}}"], "port": "{{dapr_port_user}}", - "path": [ - "v1.0-alpha1", - "configuration", - "local-config-store" - ] + "path": ["v1.0-alpha1", "configuration", "local-config-store"] } }, "response": [] @@ -300,16 +236,9 @@ "url": { "raw": "http://{{dapr_host}}:{{dapr_port_user}}/v1.0/state/local-state-store/bulk", "protocol": "http", - "host": [ - "{{dapr_host}}" - ], + "host": ["{{dapr_host}}"], "port": "{{dapr_port_user}}", - "path": [ - "v1.0", - "state", - "local-state-store", - "bulk" - ] + "path": ["v1.0", "state", "local-state-store", "bulk"] } }, "response": [] @@ -322,16 +251,9 @@ "url": { "raw": "http://{{dapr_host}}:{{dapr_port_user}}/v1.0/secrets/local-secret-store/bulk", "protocol": "http", - "host": [ - "{{dapr_host}}" - ], + "host": ["{{dapr_host}}"], "port": "{{dapr_port_user}}", - "path": [ - "v1.0", - "secrets", - "local-secret-store", - "bulk" - ] + "path": ["v1.0", "secrets", "local-secret-store", "bulk"] } }, "response": [] @@ -353,16 +275,9 @@ "url": { "raw": "http://{{dapr_host}}:{{dapr_port_user}}/v1.0/publish/{{pubsub_name}}/{{eventname}}", "protocol": "http", - "host": [ - "{{dapr_host}}" - ], + "host": ["{{dapr_host}}"], "port": "{{dapr_port_user}}", - "path": [ - "v1.0", - "publish", - "{{pubsub_name}}", - "{{eventname}}" - ] + "path": ["v1.0", "publish", "{{pubsub_name}}", "{{eventname}}"] } }, "response": [] @@ -384,19 +299,9 @@ "url": { "raw": "http://{{dapr_host}}:{{dapr_port_user}}/v1.0/invoke/user-v1/method/purista/command/compute-data", "protocol": "http", - "host": [ - "{{dapr_host}}" - ], + "host": ["{{dapr_host}}"], "port": "{{dapr_port_user}}", - "path": [ - "v1.0", - "invoke", - "user-v1", - "method", - "purista", - "command", - "compute-data" - ] + "path": ["v1.0", "invoke", "user-v1", "method", "purista", "command", "compute-data"] } }, "response": [] @@ -418,19 +323,9 @@ "url": { "raw": "http://{{dapr_host}}:{{dapr_port_user}}/v1.0/invoke/user-v1/method/purista/command/sign-up", "protocol": "http", - "host": [ - "{{dapr_host}}" - ], + "host": ["{{dapr_host}}"], "port": "{{dapr_port_user}}", - "path": [ - "v1.0", - "invoke", - "user-v1", - "method", - "purista", - "command", - "sign-up" - ] + "path": ["v1.0", "invoke", "user-v1", "method", "purista", "command", "sign-up"] } }, "response": [] @@ -460,14 +355,9 @@ "url": { "raw": "http://{{app_host}}:{{app_port_user}}/dapr/subscribe", "protocol": "http", - "host": [ - "{{app_host}}" - ], + "host": ["{{app_host}}"], "port": "{{app_port_user}}", - "path": [ - "dapr", - "subscribe" - ] + "path": ["dapr", "subscribe"] } }, "response": [] @@ -489,15 +379,9 @@ "url": { "raw": "http://{{dapr_host}}:{{app_port_user}}/api/v1/ping", "protocol": "http", - "host": [ - "{{dapr_host}}" - ], + "host": ["{{dapr_host}}"], "port": "{{app_port_user}}", - "path": [ - "api", - "v1", - "ping" - ] + "path": ["api", "v1", "ping"] } }, "response": [] @@ -538,16 +422,9 @@ "url": { "raw": "http://{{app_host}}:{{app_port_user}}/api/v1/user/signup", "protocol": "http", - "host": [ - "{{app_host}}" - ], + "host": ["{{app_host}}"], "port": "{{app_port_user}}", - "path": [ - "api", - "v1", - "user", - "signup" - ] + "path": ["api", "v1", "user", "signup"] } }, "response": [] @@ -572,15 +449,9 @@ "url": { "raw": "http://{{app_host}}:{{app_port_user}}/api/v1/user", "protocol": "http", - "host": [ - "{{app_host}}" - ], + "host": ["{{app_host}}"], "port": "{{app_port_user}}", - "path": [ - "api", - "v1", - "user" - ] + "path": ["api", "v1", "user"] } }, "response": [] @@ -605,16 +476,9 @@ "url": { "raw": "http://{{app_host}}:{{app_port_user}}/api/v1/user/{{userId}}", "protocol": "http", - "host": [ - "{{app_host}}" - ], + "host": ["{{app_host}}"], "port": "{{app_port_user}}", - "path": [ - "api", - "v1", - "user", - "{{userId}}" - ] + "path": ["api", "v1", "user", "{{userId}}"] } }, "response": [] @@ -624,4 +488,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/examples/dapr-example/src/service/ServiceEvent.enum.ts b/examples/dapr-example/src/service/ServiceEvent.enum.ts index 15959f5e3..ce86e0e9f 100644 --- a/examples/dapr-example/src/service/ServiceEvent.enum.ts +++ b/examples/dapr-example/src/service/ServiceEvent.enum.ts @@ -1,17 +1,17 @@ export enum ServiceEvent { - /** - * Emitted by user v1 command signUp: - * a new user registration - */ - NewUserRegistered = 'newUserRegistered', - /** - * Emitted by email v1 subscription sendWelcomeEmail: - * a new user registration - */ - WelcomeEmailSent = 'send a welcome mail to new registered users', - /** - * Emitted by user v1 command ping: - * ping pong - */ - Pong = 'pong', + /** + * Emitted by user v1 command signUp: + * a new user registration + */ + NewUserRegistered = 'newUserRegistered', + /** + * Emitted by email v1 subscription sendWelcomeEmail: + * a new user registration + */ + WelcomeEmailSent = 'send a welcome mail to new registered users', + /** + * Emitted by user v1 command ping: + * ping pong + */ + Pong = 'pong', } diff --git a/examples/dapr-example/src/service/email/generalEmailServiceInfo.ts b/examples/dapr-example/src/service/email/generalEmailServiceInfo.ts index c8cbc5538..446d54e53 100644 --- a/examples/dapr-example/src/service/email/generalEmailServiceInfo.ts +++ b/examples/dapr-example/src/service/email/generalEmailServiceInfo.ts @@ -1,6 +1,6 @@ import type { ServiceInfoType } from '@purista/core' export const generalEmailServiceInfo = { - serviceName: 'Email', - serviceDescription: 'sends emails to customers', + serviceName: 'Email', + serviceDescription: 'sends emails to customers', } as const satisfies Omit diff --git a/examples/dapr-example/src/service/email/v1/command/ping/ping.test.ts b/examples/dapr-example/src/service/email/v1/command/ping/ping.test.ts index c7f7e8a50..1bfa920b6 100644 --- a/examples/dapr-example/src/service/email/v1/command/ping/ping.test.ts +++ b/examples/dapr-example/src/service/email/v1/command/ping/ping.test.ts @@ -6,30 +6,30 @@ import { pingCommandBuilder } from './pingCommandBuilder.js' import type { EmailV1PingInputParameter, EmailV1PingInputPayload } from './types.js' describe('service Email version 1 - command ping', () => { - let sandbox = createSandbox() - beforeEach(() => { - sandbox = createSandbox() - }) + let sandbox = createSandbox() + beforeEach(() => { + sandbox = createSandbox() + }) - afterEach(() => { - sandbox.restore() - }) + afterEach(() => { + sandbox.restore() + }) - test('does not throw', async () => { - const service = await emailV1Service.getInstance(getEventBridgeMock(sandbox).mock, { - logger: getLoggerMock(sandbox).mock, - }) + test('does not throw', async () => { + const service = await emailV1Service.getInstance(getEventBridgeMock(sandbox).mock, { + logger: getLoggerMock(sandbox).mock, + }) - const ping = safeBind(pingCommandBuilder.getCommandFunction(), service) + const ping = safeBind(pingCommandBuilder.getCommandFunction(), service) - const payload: EmailV1PingInputPayload = 'input value' + const payload: EmailV1PingInputPayload = 'input value' - const parameter: EmailV1PingInputParameter = {} + const parameter: EmailV1PingInputParameter = {} - const context = pingCommandBuilder.getCommandContextMock(payload, parameter, sandbox) + const context = pingCommandBuilder.getCommandContextMock({ payload, parameter, sandbox }) - const result = await ping(context.mock, payload, parameter) + const result = await ping(context.mock, payload, parameter) - expect(result).toStrictEqual({ pong: 'input value' }) - }) + expect(result).toStrictEqual({ pong: 'input value' }) + }) }) diff --git a/examples/dapr-example/src/service/email/v1/command/ping/pingCommandBuilder.ts b/examples/dapr-example/src/service/email/v1/command/ping/pingCommandBuilder.ts index 067e2b13c..b17ad4107 100644 --- a/examples/dapr-example/src/service/email/v1/command/ping/pingCommandBuilder.ts +++ b/examples/dapr-example/src/service/email/v1/command/ping/pingCommandBuilder.ts @@ -1,20 +1,20 @@ import { ServiceEvent } from '../../../../ServiceEvent.enum.js' import { emailV1ServiceBuilder } from '../../emailV1ServiceBuilder.js' import { - emailV1PingInputParameterSchema, - emailV1PingInputPayloadSchema, - emailV1PingOutputPayloadSchema, + emailV1PingInputParameterSchema, + emailV1PingInputPayloadSchema, + emailV1PingOutputPayloadSchema, } from './schema.js' export const pingCommandBuilder = emailV1ServiceBuilder - .getCommandBuilder('ping', 'ping pong') - .setSuccessEventName(ServiceEvent.Pong) - .addPayloadSchema(emailV1PingInputPayloadSchema) - .addParameterSchema(emailV1PingInputParameterSchema) - .addOutputSchema(emailV1PingOutputPayloadSchema) - .setCommandFunction(async function (_context, payload, _parameter) { - // add your business logic here - return { - pong: payload, - } - }) + .getCommandBuilder('ping', 'ping pong') + .setSuccessEventName(ServiceEvent.Pong) + .addPayloadSchema(emailV1PingInputPayloadSchema) + .addParameterSchema(emailV1PingInputParameterSchema) + .addOutputSchema(emailV1PingOutputPayloadSchema) + .setCommandFunction(async function (_context, payload, _parameter) { + // add your business logic here + return { + pong: payload, + } + }) diff --git a/examples/dapr-example/src/service/email/v1/command/ping/types.ts b/examples/dapr-example/src/service/email/v1/command/ping/types.ts index a1aa65cb7..2aa6b3bdb 100644 --- a/examples/dapr-example/src/service/email/v1/command/ping/types.ts +++ b/examples/dapr-example/src/service/email/v1/command/ping/types.ts @@ -1,9 +1,9 @@ import type { z } from 'zod' import type { - emailV1PingInputParameterSchema, - emailV1PingInputPayloadSchema, - emailV1PingOutputPayloadSchema, + emailV1PingInputParameterSchema, + emailV1PingInputPayloadSchema, + emailV1PingOutputPayloadSchema, } from './schema.js' export type EmailV1PingInputParameter = z.input diff --git a/examples/dapr-example/src/service/email/v1/emailV1Service.test.ts b/examples/dapr-example/src/service/email/v1/emailV1Service.test.ts index 5b9b12e07..f60972446 100644 --- a/examples/dapr-example/src/service/email/v1/emailV1Service.test.ts +++ b/examples/dapr-example/src/service/email/v1/emailV1Service.test.ts @@ -1,11 +1,7 @@ import { emailV1Service as service } from './emailV1Service.js' describe('service email version 1', () => { - it('has valid commands', () => { - service.validateCommandDefinitions() - }) - - it('has valid subscriptions', () => { - service.validateSubscriptionDefinitions() - }) + it('has valid setup', () => { + service.testServiceSetup() + }) }) diff --git a/examples/dapr-example/src/service/email/v1/emailV1Service.ts b/examples/dapr-example/src/service/email/v1/emailV1Service.ts index 339fe5db8..03dcf2769 100644 --- a/examples/dapr-example/src/service/email/v1/emailV1Service.ts +++ b/examples/dapr-example/src/service/email/v1/emailV1Service.ts @@ -13,5 +13,5 @@ const commandDefinitions: CommandDefinitionList = [pingCommandBuilder.getDe const subscriptionDefinitions: SubscriptionDefinitionList = [sendWelcomeEmailSubscriptionBuilder.getDefinition()] export const emailV1Service = emailV1ServiceBuilder - .addCommandDefinition(...commandDefinitions) - .addSubscriptionDefinition(...subscriptionDefinitions) + .addCommandDefinition(...commandDefinitions) + .addSubscriptionDefinition(...subscriptionDefinitions) diff --git a/examples/dapr-example/src/service/email/v1/emailV1ServiceBuilder.ts b/examples/dapr-example/src/service/email/v1/emailV1ServiceBuilder.ts index ab2fd3b9a..926b0389e 100644 --- a/examples/dapr-example/src/service/email/v1/emailV1ServiceBuilder.ts +++ b/examples/dapr-example/src/service/email/v1/emailV1ServiceBuilder.ts @@ -5,12 +5,10 @@ import { generalEmailServiceInfo } from '../generalEmailServiceInfo.js' import { emailServiceV1ConfigSchema } from './emailServiceConfig.js' export const emailServiceInfo = { - serviceVersion: '1', - ...generalEmailServiceInfo, + serviceVersion: '1', + ...generalEmailServiceInfo, } as const satisfies ServiceInfoType // create a service builder instance and assign service config schema and default config. -export const emailV1ServiceBuilder = new ServiceBuilder(emailServiceInfo) - .setConfigSchema(emailServiceV1ConfigSchema) - .setDefaultConfig({}) +export const emailV1ServiceBuilder = new ServiceBuilder(emailServiceInfo).setConfigSchema(emailServiceV1ConfigSchema) diff --git a/examples/dapr-example/src/service/email/v1/subscription/sendWelcomeEmail/schema.ts b/examples/dapr-example/src/service/email/v1/subscription/sendWelcomeEmail/schema.ts index 97bd10475..227f140eb 100644 --- a/examples/dapr-example/src/service/email/v1/subscription/sendWelcomeEmail/schema.ts +++ b/examples/dapr-example/src/service/email/v1/subscription/sendWelcomeEmail/schema.ts @@ -2,9 +2,9 @@ import { z } from 'zod' // define the input payload export const emailV1SendWelcomeEmailInputPayloadSchema = z.object({ - userId: z.string().uuid(), + userId: z.string().uuid(), }) export const emailV1SendWelcomeEmailOutputPayloadSchema = z.object({ - userId: z.string().uuid(), + userId: z.string().uuid(), }) diff --git a/examples/dapr-example/src/service/email/v1/subscription/sendWelcomeEmail/sendWelcomeEmail.test.ts b/examples/dapr-example/src/service/email/v1/subscription/sendWelcomeEmail/sendWelcomeEmail.test.ts index 8b8b1bc3d..9c757b306 100644 --- a/examples/dapr-example/src/service/email/v1/subscription/sendWelcomeEmail/sendWelcomeEmail.test.ts +++ b/examples/dapr-example/src/service/email/v1/subscription/sendWelcomeEmail/sendWelcomeEmail.test.ts @@ -7,54 +7,54 @@ import { sendWelcomeEmailSubscriptionBuilder } from './sendWelcomeEmailSubscript import type { EmailV1SendWelcomeEmailInputPayload } from './types.js' describe('service Email version 1 - subscription sendWelcomeEmail', () => { - let sandbox = createSandbox() - beforeEach(() => { - sandbox = createSandbox() - }) - - afterEach(() => { - sandbox.restore() - }) - - test('sends an email', async () => { - // create a service instance to be bind to the subscription function - const service = await emailV1Service.getInstance(getEventBridgeMock(sandbox).mock, { - logger: getLoggerMock(sandbox).mock, - }) - - // get the subscription function and bind to service instance to work properly - const sendWelcomeEmail = safeBind(sendWelcomeEmailSubscriptionBuilder.getSubscriptionFunction(), service) - - const userMock: User = { - email: 'email@example.com', - name: 'test user', - password: 'password', - userId: 'a5fef052-911c-472c-ac25-e2da327f0af5', - } - - // define the test input payload - const payload: EmailV1SendWelcomeEmailInputPayload = { userId: userMock.userId } - - // define the test input parameter - const parameter = undefined as unknown as Readonly - - // create a mock message with the expected input for the subscription function - const message = getCommandSuccessMessageMock(payload) - - // create a subscription context for the subscription function - const context = sendWelcomeEmailSubscriptionBuilder.getSubscriptionContextMock(message, sandbox) - - context.stubs.service.User['1'].getUserById.resolves(userMock) - context.stubs.getConfig.resolves({ emailProviderUrl: 'https://example.com' }) - context.stubs.getSecret.resolves({ emailProviderAuthToken: 'secret_token' }) - - // execute the subscription function - const result = await sendWelcomeEmail(context.mock, payload, parameter) - - expect( - context.stubs.logger.info.calledWith('Using email provider https://example.com with token secret_token'), - ).toBeTruthy() - expect(context.stubs.logger.info.calledWith('Welcome email to user sent to ' + userMock.email)).toBeTruthy() - expect(result).toStrictEqual({ userId: userMock.userId }) - }) + let sandbox = createSandbox() + beforeEach(() => { + sandbox = createSandbox() + }) + + afterEach(() => { + sandbox.restore() + }) + + test('sends an email', async () => { + // create a service instance to be bind to the subscription function + const service = await emailV1Service.getInstance(getEventBridgeMock(sandbox).mock, { + logger: getLoggerMock(sandbox).mock, + }) + + // get the subscription function and bind to service instance to work properly + const sendWelcomeEmail = safeBind(sendWelcomeEmailSubscriptionBuilder.getSubscriptionFunction(), service) + + const userMock: User = { + email: 'email@example.com', + name: 'test user', + password: 'password', + userId: 'a5fef052-911c-472c-ac25-e2da327f0af5', + } + + // define the test input payload + const payload: EmailV1SendWelcomeEmailInputPayload = { userId: userMock.userId } + + // define the test input parameter + const parameter = undefined as unknown as Readonly + + // create a mock message with the expected input for the subscription function + const message = getCommandSuccessMessageMock(payload) + + // create a subscription context for the subscription function + const context = sendWelcomeEmailSubscriptionBuilder.getSubscriptionContextMock({ message, sandbox }) + + context.stubs.service.User['1'].getUserById.resolves(userMock) + context.stubs.getConfig.resolves({ emailProviderUrl: 'https://example.com' }) + context.stubs.getSecret.resolves({ emailProviderAuthToken: 'secret_token' }) + + // execute the subscription function + const result = await sendWelcomeEmail(context.mock, payload, parameter) + + expect( + context.stubs.logger.info.calledWith('Using email provider https://example.com with token secret_token'), + ).toBeTruthy() + expect(context.stubs.logger.info.calledWith(`Welcome email to user sent to ${userMock.email}`)).toBeTruthy() + expect(result).toStrictEqual({ userId: userMock.userId }) + }) }) diff --git a/examples/dapr-example/src/service/email/v1/subscription/sendWelcomeEmail/sendWelcomeEmailSubscriptionBuilder.ts b/examples/dapr-example/src/service/email/v1/subscription/sendWelcomeEmail/sendWelcomeEmailSubscriptionBuilder.ts index 9f3845574..3faab7781 100644 --- a/examples/dapr-example/src/service/email/v1/subscription/sendWelcomeEmail/sendWelcomeEmailSubscriptionBuilder.ts +++ b/examples/dapr-example/src/service/email/v1/subscription/sendWelcomeEmail/sendWelcomeEmailSubscriptionBuilder.ts @@ -1,35 +1,35 @@ import { ServiceEvent } from '../../../../ServiceEvent.enum.js' import { - userV1GetUserByIdInputParameterSchema, - userV1GetUserByIdInputPayloadSchema, - userV1GetUserByIdOutputPayloadSchema, + userV1GetUserByIdInputParameterSchema, + userV1GetUserByIdInputPayloadSchema, + userV1GetUserByIdOutputPayloadSchema, } from '../../../../user/v1/command/getUserById/index.js' import { emailV1ServiceBuilder } from '../../emailV1ServiceBuilder.js' import { emailV1SendWelcomeEmailInputPayloadSchema, emailV1SendWelcomeEmailOutputPayloadSchema } from './schema.js' export const sendWelcomeEmailSubscriptionBuilder = emailV1ServiceBuilder - .getSubscriptionBuilder('sendWelcomeEmail', 'send a welcome mail to new registered users') - .subscribeToEvent(ServiceEvent.NewUserRegistered) - .addPayloadSchema(emailV1SendWelcomeEmailInputPayloadSchema) - .addOutputSchema(ServiceEvent.WelcomeEmailSent, emailV1SendWelcomeEmailOutputPayloadSchema) - .canInvoke( - 'User', - '1', - 'getUserById', - userV1GetUserByIdOutputPayloadSchema, - userV1GetUserByIdInputPayloadSchema, - userV1GetUserByIdInputParameterSchema, - ) - .setSubscriptionFunction(async function (context, payload, _parameter) { - context.logger.info('sendWelcomeEmail starting') - const config = await context.configs.getConfig('emailProviderUrl') - const secrets = await context.secrets.getSecret('emailProviderAuthToken') + .getSubscriptionBuilder('sendWelcomeEmail', 'send a welcome mail to new registered users') + .subscribeToEvent(ServiceEvent.NewUserRegistered) + .addPayloadSchema(emailV1SendWelcomeEmailInputPayloadSchema) + .addOutputSchema(ServiceEvent.WelcomeEmailSent, emailV1SendWelcomeEmailOutputPayloadSchema) + .canInvoke( + 'User', + '1', + 'getUserById', + userV1GetUserByIdOutputPayloadSchema, + userV1GetUserByIdInputPayloadSchema, + userV1GetUserByIdInputParameterSchema, + ) + .setSubscriptionFunction(async function (context, payload, _parameter) { + context.logger.info('sendWelcomeEmail starting') + const config = await context.configs.getConfig('emailProviderUrl') + const secrets = await context.secrets.getSecret('emailProviderAuthToken') - context.logger.info(`Using email provider ${config.emailProviderUrl} with token ${secrets.emailProviderAuthToken}`) + context.logger.info(`Using email provider ${config.emailProviderUrl} with token ${secrets.emailProviderAuthToken}`) - const user = await context.service.User['1'].getUserById(undefined, payload) - // add your business logic here - context.logger.info(`Welcome email to user sent to ${user.email}`) + const user = await context.service.User['1'].getUserById(undefined, payload) + // add your business logic here + context.logger.info(`Welcome email to user sent to ${user.email}`) - return payload - }) + return payload + }) diff --git a/examples/dapr-example/src/service/user/generalUserServiceInfo.ts b/examples/dapr-example/src/service/user/generalUserServiceInfo.ts index b5e2bafd7..770f5c84e 100644 --- a/examples/dapr-example/src/service/user/generalUserServiceInfo.ts +++ b/examples/dapr-example/src/service/user/generalUserServiceInfo.ts @@ -1,6 +1,6 @@ import type { ServiceInfoType } from '@purista/core' export const generalUserServiceInfo = { - serviceName: 'User', - serviceDescription: 'manage user information', + serviceName: 'User', + serviceDescription: 'manage user information', } as const satisfies Omit diff --git a/examples/dapr-example/src/service/user/v1/command/computeData/computeData.test.ts b/examples/dapr-example/src/service/user/v1/command/computeData/computeData.test.ts index 05358a7b7..e2132fcc2 100644 --- a/examples/dapr-example/src/service/user/v1/command/computeData/computeData.test.ts +++ b/examples/dapr-example/src/service/user/v1/command/computeData/computeData.test.ts @@ -6,30 +6,30 @@ import { computeDataCommandBuilder } from './computeDataCommandBuilder.js' import type { UserV1ComputeDataInputParameter, UserV1ComputeDataInputPayload } from './types.js' describe('service User version 1 - command computeData', () => { - let sandbox = createSandbox() - beforeEach(() => { - sandbox = createSandbox() - }) + let sandbox = createSandbox() + beforeEach(() => { + sandbox = createSandbox() + }) - afterEach(() => { - sandbox.restore() - }) + afterEach(() => { + sandbox.restore() + }) - test('does not throw', async () => { - const service = await userV1Service.getInstance(getEventBridgeMock(sandbox).mock, { - logger: getLoggerMock(sandbox).mock, - }) + test('does not throw', async () => { + const service = await userV1Service.getInstance(getEventBridgeMock(sandbox).mock, { + logger: getLoggerMock(sandbox).mock, + }) - const computeData = safeBind(computeDataCommandBuilder.getCommandFunction(), service) + const computeData = safeBind(computeDataCommandBuilder.getCommandFunction(), service) - const payload: UserV1ComputeDataInputPayload = 'example value' + const payload: UserV1ComputeDataInputPayload = 'example value' - const parameter: UserV1ComputeDataInputParameter = {} + const parameter: UserV1ComputeDataInputParameter = {} - const context = computeDataCommandBuilder.getCommandContextMock(payload, parameter, sandbox) + const context = computeDataCommandBuilder.getCommandContextMock({ payload, parameter, sandbox }) - const result = await computeData(context.mock, payload, parameter) + const result = await computeData(context.mock, payload, parameter) - expect(result).toStrictEqual({ invoked: 'example value' }) - }) + expect(result).toStrictEqual({ invoked: 'example value' }) + }) }) diff --git a/examples/dapr-example/src/service/user/v1/command/computeData/computeDataCommandBuilder.ts b/examples/dapr-example/src/service/user/v1/command/computeData/computeDataCommandBuilder.ts index 4fcd04d9e..dcbc2de54 100644 --- a/examples/dapr-example/src/service/user/v1/command/computeData/computeDataCommandBuilder.ts +++ b/examples/dapr-example/src/service/user/v1/command/computeData/computeDataCommandBuilder.ts @@ -1,18 +1,18 @@ import { userV1ServiceBuilder } from '../../userV1ServiceBuilder.js' import { - userV1ComputeDataInputParameterSchema, - userV1ComputeDataInputPayloadSchema, - userV1ComputeDataOutputPayloadSchema, + userV1ComputeDataInputParameterSchema, + userV1ComputeDataInputPayloadSchema, + userV1ComputeDataOutputPayloadSchema, } from './schema.js' export const computeDataCommandBuilder = userV1ServiceBuilder - .getCommandBuilder('computeData', 'simulates something to demonstrate invoke') - .addPayloadSchema(userV1ComputeDataInputPayloadSchema) - .addParameterSchema(userV1ComputeDataInputParameterSchema) - .addOutputSchema(userV1ComputeDataOutputPayloadSchema) - .setCommandFunction(async function ({ logger }, payload, parameter) { - logger.debug({ payload, parameter }) - return { - invoked: payload, - } - }) + .getCommandBuilder('computeData', 'simulates something to demonstrate invoke') + .addPayloadSchema(userV1ComputeDataInputPayloadSchema) + .addParameterSchema(userV1ComputeDataInputParameterSchema) + .addOutputSchema(userV1ComputeDataOutputPayloadSchema) + .setCommandFunction(async function ({ logger }, payload, parameter) { + logger.debug({ payload, parameter }) + return { + invoked: payload, + } + }) diff --git a/examples/dapr-example/src/service/user/v1/command/computeData/schema.ts b/examples/dapr-example/src/service/user/v1/command/computeData/schema.ts index 3dc26e560..2d84d9aeb 100644 --- a/examples/dapr-example/src/service/user/v1/command/computeData/schema.ts +++ b/examples/dapr-example/src/service/user/v1/command/computeData/schema.ts @@ -3,7 +3,7 @@ import { z } from 'zod' // define the input parameters export const userV1ComputeDataInputParameterSchema = extendApi(z.object({}), { - title: 'compute data input parameter schema', + title: 'compute data input parameter schema', }) // define the input payload diff --git a/examples/dapr-example/src/service/user/v1/command/computeData/types.ts b/examples/dapr-example/src/service/user/v1/command/computeData/types.ts index 3fd73dfef..b16021373 100644 --- a/examples/dapr-example/src/service/user/v1/command/computeData/types.ts +++ b/examples/dapr-example/src/service/user/v1/command/computeData/types.ts @@ -1,9 +1,9 @@ import type { z } from 'zod' import type { - userV1ComputeDataInputParameterSchema, - userV1ComputeDataInputPayloadSchema, - userV1ComputeDataOutputPayloadSchema, + userV1ComputeDataInputParameterSchema, + userV1ComputeDataInputPayloadSchema, + userV1ComputeDataOutputPayloadSchema, } from './schema.js' export type UserV1ComputeDataInputParameter = z.input diff --git a/examples/dapr-example/src/service/user/v1/command/getAllUsers/getAllUsers.test.ts b/examples/dapr-example/src/service/user/v1/command/getAllUsers/getAllUsers.test.ts index 3917b01dd..2e1b8032e 100644 --- a/examples/dapr-example/src/service/user/v1/command/getAllUsers/getAllUsers.test.ts +++ b/examples/dapr-example/src/service/user/v1/command/getAllUsers/getAllUsers.test.ts @@ -8,41 +8,41 @@ import { getAllUsersCommandBuilder } from './getAllUsersCommandBuilder.js' import type { UserV1GetAllUsersInputParameter, UserV1GetAllUsersInputPayload } from './types.js' describe('service User version 1 - command getAllUsers', () => { - let sandbox = createSandbox() - beforeEach(() => { - sandbox = createSandbox() - }) + let sandbox = createSandbox() + beforeEach(() => { + sandbox = createSandbox() + }) - afterEach(() => { - sandbox.restore() - }) + afterEach(() => { + sandbox.restore() + }) - test('does not throw', async () => { - const service = await userV1Service.getInstance(getEventBridgeMock(sandbox).mock, { - logger: getLoggerMock(sandbox).mock, - }) + test('does not throw', async () => { + const service = await userV1Service.getInstance(getEventBridgeMock(sandbox).mock, { + logger: getLoggerMock(sandbox).mock, + }) - const getAllUsers = safeBind(getAllUsersCommandBuilder.getCommandFunction(), service) + const getAllUsers = safeBind(getAllUsersCommandBuilder.getCommandFunction(), service) - const payload: UserV1GetAllUsersInputPayload = undefined + const payload: UserV1GetAllUsersInputPayload = undefined - const parameter: UserV1GetAllUsersInputParameter = {} + const parameter: UserV1GetAllUsersInputParameter = {} - const context = getAllUsersCommandBuilder.getCommandContextMock(payload, parameter, sandbox) + const context = getAllUsersCommandBuilder.getCommandContextMock({ payload, parameter, sandbox }) - const userMock: User = { - email: 'email@example.com', - name: 'test user', - password: 'password', - userId: 'a5fef052-911c-472c-ac25-e2da327f0af5', - } + const userMock: User = { + email: 'email@example.com', + name: 'test user', + password: 'password', + userId: 'a5fef052-911c-472c-ac25-e2da327f0af5', + } - context.stubs.getState.resolves({ - [StateStoreKey.Users]: [userMock], - }) + context.stubs.getState.resolves({ + [StateStoreKey.Users]: [userMock], + }) - const result = await getAllUsers(context.mock, payload, parameter) + const result = await getAllUsers(context.mock, payload, parameter) - expect(result).toStrictEqual([{ name: userMock.name, email: userMock.email, userId: userMock.userId }]) - }) + expect(result).toStrictEqual([{ name: userMock.name, email: userMock.email, userId: userMock.userId }]) + }) }) diff --git a/examples/dapr-example/src/service/user/v1/command/getAllUsers/getAllUsersCommandBuilder.ts b/examples/dapr-example/src/service/user/v1/command/getAllUsers/getAllUsersCommandBuilder.ts index 482dda298..911cde6bf 100644 --- a/examples/dapr-example/src/service/user/v1/command/getAllUsers/getAllUsersCommandBuilder.ts +++ b/examples/dapr-example/src/service/user/v1/command/getAllUsers/getAllUsersCommandBuilder.ts @@ -2,20 +2,20 @@ import type { User } from '../../../../../types/index.js' import { StateStoreKey } from '../../../../../types/index.js' import { userV1ServiceBuilder } from '../../userV1ServiceBuilder.js' import { - userV1GetAllUsersInputParameterSchema, - userV1GetAllUsersInputPayloadSchema, - userV1GetAllUsersOutputPayloadSchema, + userV1GetAllUsersInputParameterSchema, + userV1GetAllUsersInputPayloadSchema, + userV1GetAllUsersOutputPayloadSchema, } from './schema.js' export const getAllUsersCommandBuilder = userV1ServiceBuilder - .getCommandBuilder('getAllUsers', 'returns a list of registered users') - .addPayloadSchema(userV1GetAllUsersInputPayloadSchema) - .addParameterSchema(userV1GetAllUsersInputParameterSchema) - .addOutputSchema(userV1GetAllUsersOutputPayloadSchema) - .exposeAsHttpEndpoint('GET', '/user') - .setCommandFunction(async function (context, _payload, _parameter) { - const result = (await context.states.getState(StateStoreKey.Users)) as { [StateStoreKey.Users]: User[] | undefined } - const users = result.users ?? [] + .getCommandBuilder('getAllUsers', 'returns a list of registered users') + .addPayloadSchema(userV1GetAllUsersInputPayloadSchema) + .addParameterSchema(userV1GetAllUsersInputParameterSchema) + .addOutputSchema(userV1GetAllUsersOutputPayloadSchema) + .exposeAsHttpEndpoint('GET', '/user') + .setCommandFunction(async function (context, _payload, _parameter) { + const result = (await context.states.getState(StateStoreKey.Users)) as { [StateStoreKey.Users]: User[] | undefined } + const users = result.users ?? [] - return users - }) + return users + }) diff --git a/examples/dapr-example/src/service/user/v1/command/getAllUsers/schema.ts b/examples/dapr-example/src/service/user/v1/command/getAllUsers/schema.ts index 32c736fc2..91a45623c 100644 --- a/examples/dapr-example/src/service/user/v1/command/getAllUsers/schema.ts +++ b/examples/dapr-example/src/service/user/v1/command/getAllUsers/schema.ts @@ -3,22 +3,22 @@ import { z } from 'zod' // define the input parameters export const userV1GetAllUsersInputParameterSchema = extendApi(z.object({}), { - title: 'get all users input parameter schema', + title: 'get all users input parameter schema', }) // define the input payload export const userV1GetAllUsersInputPayloadSchema = z.undefined() export const userV1GetAllUsersUserEntrySchema = z.object({ - userId: extendApi(z.string().uuid(), { title: 'the user id', example: 'a5fef052-911c-472c-ac25-e2da327f0af5' }), - email: extendApi(z.string().email(), { title: 'the email of the user to register', example: 'user@email.com' }), - name: extendApi(z.string().min(3), { - title: 'the name of the user to register', - example: 'User', - }), + userId: extendApi(z.string().uuid(), { title: 'the user id', example: 'a5fef052-911c-472c-ac25-e2da327f0af5' }), + email: extendApi(z.string().email(), { title: 'the email of the user to register', example: 'user@email.com' }), + name: extendApi(z.string().min(3), { + title: 'the name of the user to register', + example: 'User', + }), }) // define the output payload export const userV1GetAllUsersOutputPayloadSchema = extendApi(z.array(userV1GetAllUsersUserEntrySchema), { - title: 'get all users output payload schema', + title: 'get all users output payload schema', }) diff --git a/examples/dapr-example/src/service/user/v1/command/getAllUsers/types.ts b/examples/dapr-example/src/service/user/v1/command/getAllUsers/types.ts index c8486af0d..4e88b232e 100644 --- a/examples/dapr-example/src/service/user/v1/command/getAllUsers/types.ts +++ b/examples/dapr-example/src/service/user/v1/command/getAllUsers/types.ts @@ -1,9 +1,9 @@ import type { z } from 'zod' import type { - userV1GetAllUsersInputParameterSchema, - userV1GetAllUsersInputPayloadSchema, - userV1GetAllUsersOutputPayloadSchema, + userV1GetAllUsersInputParameterSchema, + userV1GetAllUsersInputPayloadSchema, + userV1GetAllUsersOutputPayloadSchema, } from './schema.js' export type UserV1GetAllUsersInputParameter = z.input diff --git a/examples/dapr-example/src/service/user/v1/command/getUserById/getUserById.test.ts b/examples/dapr-example/src/service/user/v1/command/getUserById/getUserById.test.ts index ef9664911..b9bd14727 100644 --- a/examples/dapr-example/src/service/user/v1/command/getUserById/getUserById.test.ts +++ b/examples/dapr-example/src/service/user/v1/command/getUserById/getUserById.test.ts @@ -8,68 +8,68 @@ import { getUserByIdCommandBuilder } from './getUserByIdCommandBuilder.js' import type { UserV1GetUserByIdInputParameter, UserV1GetUserByIdInputPayload } from './types.js' describe('service User version 1 - command getUserById', () => { - let sandbox = createSandbox() - beforeEach(() => { - sandbox = createSandbox() - }) + let sandbox = createSandbox() + beforeEach(() => { + sandbox = createSandbox() + }) - afterEach(() => { - sandbox.restore() - }) + afterEach(() => { + sandbox.restore() + }) - test('returns a user', async () => { - const service = await userV1Service.getInstance(getEventBridgeMock(sandbox).mock, { - logger: getLoggerMock(sandbox).mock, - }) + test('returns a user', async () => { + const service = await userV1Service.getInstance(getEventBridgeMock(sandbox).mock, { + logger: getLoggerMock(sandbox).mock, + }) - const getUserById = safeBind(getUserByIdCommandBuilder.getCommandFunction(), service) + const getUserById = safeBind(getUserByIdCommandBuilder.getCommandFunction(), service) - const payload: UserV1GetUserByIdInputPayload = undefined + const payload: UserV1GetUserByIdInputPayload = undefined - const userMock: User = { - email: 'email@example.com', - name: 'test user', - password: 'password', - userId: 'a5fef052-911c-472c-ac25-e2da327f0af5', - } + const userMock: User = { + email: 'email@example.com', + name: 'test user', + password: 'password', + userId: 'a5fef052-911c-472c-ac25-e2da327f0af5', + } - const parameter: UserV1GetUserByIdInputParameter = { - userId: userMock.userId, - } + const parameter: UserV1GetUserByIdInputParameter = { + userId: userMock.userId, + } - const context = getUserByIdCommandBuilder.getCommandContextMock(payload, parameter, sandbox) + const context = getUserByIdCommandBuilder.getCommandContextMock({ payload, parameter, sandbox }) - context.stubs.getState.resolves({ [StateStoreKey.Users]: [userMock] }) + context.stubs.getState.resolves({ [StateStoreKey.Users]: [userMock] }) - const result = await getUserById(context.mock, payload, parameter) + const result = await getUserById(context.mock, payload, parameter) - expect(result).toStrictEqual({ email: userMock.email, name: userMock.name, userId: userMock.userId }) - }) + expect(result).toStrictEqual({ email: userMock.email, name: userMock.name, userId: userMock.userId }) + }) - test('throws if user can not be found', async () => { - const service = await userV1Service.getInstance(getEventBridgeMock(sandbox).mock, { - logger: getLoggerMock(sandbox).mock, - }) + test('throws if user can not be found', async () => { + const service = await userV1Service.getInstance(getEventBridgeMock(sandbox).mock, { + logger: getLoggerMock(sandbox).mock, + }) - const getUserById = safeBind(getUserByIdCommandBuilder.getCommandFunction(), service) + const getUserById = safeBind(getUserByIdCommandBuilder.getCommandFunction(), service) - const payload: UserV1GetUserByIdInputPayload = undefined + const payload: UserV1GetUserByIdInputPayload = undefined - const userMock: User = { - email: 'email@example.com', - name: 'test user', - password: 'password', - userId: 'a5fef052-911c-472c-ac25-e2da327f0af5', - } + const userMock: User = { + email: 'email@example.com', + name: 'test user', + password: 'password', + userId: 'a5fef052-911c-472c-ac25-e2da327f0af5', + } - const parameter: UserV1GetUserByIdInputParameter = { - userId: userMock.userId, - } + const parameter: UserV1GetUserByIdInputParameter = { + userId: userMock.userId, + } - const context = getUserByIdCommandBuilder.getCommandContextMock(payload, parameter, sandbox) + const context = getUserByIdCommandBuilder.getCommandContextMock({ payload, parameter, sandbox }) - context.stubs.getState.resolves({ [StateStoreKey.Users]: [] }) + context.stubs.getState.resolves({ [StateStoreKey.Users]: [] }) - await expect(getUserById(context.mock, payload, parameter)).rejects.toThrow('user could not be found') - }) + await expect(getUserById(context.mock, payload, parameter)).rejects.toThrow('user could not be found') + }) }) diff --git a/examples/dapr-example/src/service/user/v1/command/getUserById/getUserByIdCommandBuilder.ts b/examples/dapr-example/src/service/user/v1/command/getUserById/getUserByIdCommandBuilder.ts index 8d8e25d5f..5256e3242 100644 --- a/examples/dapr-example/src/service/user/v1/command/getUserById/getUserByIdCommandBuilder.ts +++ b/examples/dapr-example/src/service/user/v1/command/getUserById/getUserByIdCommandBuilder.ts @@ -4,26 +4,26 @@ import type { User } from '../../../../../types/index.js' import { StateStoreKey } from '../../../../../types/index.js' import { userV1ServiceBuilder } from '../../userV1ServiceBuilder.js' import { - userV1GetUserByIdInputParameterSchema, - userV1GetUserByIdInputPayloadSchema, - userV1GetUserByIdOutputPayloadSchema, + userV1GetUserByIdInputParameterSchema, + userV1GetUserByIdInputPayloadSchema, + userV1GetUserByIdOutputPayloadSchema, } from './schema.js' export const getUserByIdCommandBuilder = userV1ServiceBuilder - .getCommandBuilder('getUserById', 'returns the user given by the user id') - .addPayloadSchema(userV1GetUserByIdInputPayloadSchema) - .addParameterSchema(userV1GetUserByIdInputParameterSchema) - .addOutputSchema(userV1GetUserByIdOutputPayloadSchema) - .exposeAsHttpEndpoint('GET', 'user/:userId') - .setCommandFunction(async function (context, _payload, parameter) { - const result = (await context.states.getState(StateStoreKey.Users)) as { [StateStoreKey.Users]: User[] | undefined } - const users = result.users ?? [] + .getCommandBuilder('getUserById', 'returns the user given by the user id') + .addPayloadSchema(userV1GetUserByIdInputPayloadSchema) + .addParameterSchema(userV1GetUserByIdInputParameterSchema) + .addOutputSchema(userV1GetUserByIdOutputPayloadSchema) + .exposeAsHttpEndpoint('GET', 'user/:userId') + .setCommandFunction(async function (context, _payload, parameter) { + const result = (await context.states.getState(StateStoreKey.Users)) as { [StateStoreKey.Users]: User[] | undefined } + const users = result.users ?? [] - const user = users.find((user) => (user.userId = parameter.userId)) + const user = users.find(user => user.userId === parameter.userId) - if (!user) { - throw new HandledError(StatusCode.NotFound, 'user could not be found', { userId: parameter.userId }) - } + if (!user) { + throw new HandledError(StatusCode.NotFound, 'user could not be found', { userId: parameter.userId }) + } - return user - }) + return user + }) diff --git a/examples/dapr-example/src/service/user/v1/command/getUserById/schema.ts b/examples/dapr-example/src/service/user/v1/command/getUserById/schema.ts index 87b2549bb..06a7616ec 100644 --- a/examples/dapr-example/src/service/user/v1/command/getUserById/schema.ts +++ b/examples/dapr-example/src/service/user/v1/command/getUserById/schema.ts @@ -3,10 +3,10 @@ import { z } from 'zod' // define the input parameters export const userV1GetUserByIdInputParameterSchema = extendApi( - z.object({ - userId: extendApi(z.string().uuid(), { title: 'the user id', example: 'a5fef052-911c-472c-ac25-e2da327f0af5' }), - }), - { title: 'get user by id input payload schema' }, + z.object({ + userId: extendApi(z.string().uuid(), { title: 'the user id', example: 'a5fef052-911c-472c-ac25-e2da327f0af5' }), + }), + { title: 'get user by id input payload schema' }, ) // define the input payload @@ -14,13 +14,13 @@ export const userV1GetUserByIdInputPayloadSchema = z.undefined() // define the output payload export const userV1GetUserByIdOutputPayloadSchema = extendApi( - z.object({ - userId: extendApi(z.string().uuid(), { title: 'the user id', example: 'a5fef052-911c-472c-ac25-e2da327f0af5' }), - email: extendApi(z.string().email(), { title: 'the email of the user to register', example: 'user@email.com' }), - name: extendApi(z.string().min(3), { - title: 'the name of the user to register', - example: 'User', - }), - }), - { title: 'the sign up input' }, + z.object({ + userId: extendApi(z.string().uuid(), { title: 'the user id', example: 'a5fef052-911c-472c-ac25-e2da327f0af5' }), + email: extendApi(z.string().email(), { title: 'the email of the user to register', example: 'user@email.com' }), + name: extendApi(z.string().min(3), { + title: 'the name of the user to register', + example: 'User', + }), + }), + { title: 'the sign up input' }, ) diff --git a/examples/dapr-example/src/service/user/v1/command/getUserById/types.ts b/examples/dapr-example/src/service/user/v1/command/getUserById/types.ts index 7a3c6290c..59d70c337 100644 --- a/examples/dapr-example/src/service/user/v1/command/getUserById/types.ts +++ b/examples/dapr-example/src/service/user/v1/command/getUserById/types.ts @@ -1,9 +1,9 @@ import type { z } from 'zod' import type { - userV1GetUserByIdInputParameterSchema, - userV1GetUserByIdInputPayloadSchema, - userV1GetUserByIdOutputPayloadSchema, + userV1GetUserByIdInputParameterSchema, + userV1GetUserByIdInputPayloadSchema, + userV1GetUserByIdOutputPayloadSchema, } from './schema.js' export type UserV1GetUserByIdInputParameter = z.input diff --git a/examples/dapr-example/src/service/user/v1/command/ping/ping.test.ts b/examples/dapr-example/src/service/user/v1/command/ping/ping.test.ts index 46b0c4e01..e443f0743 100644 --- a/examples/dapr-example/src/service/user/v1/command/ping/ping.test.ts +++ b/examples/dapr-example/src/service/user/v1/command/ping/ping.test.ts @@ -6,32 +6,32 @@ import { pingCommandBuilder } from './pingCommandBuilder.js' import type { UserV1PingInputParameter, UserV1PingInputPayload } from './types.js' describe('service User version 1 - command ping', () => { - let sandbox = createSandbox() - beforeEach(() => { - sandbox = createSandbox() - }) + let sandbox = createSandbox() + beforeEach(() => { + sandbox = createSandbox() + }) - afterEach(() => { - sandbox.restore() - }) + afterEach(() => { + sandbox.restore() + }) - test('does not throw', async () => { - const service = await userV1Service.getInstance(getEventBridgeMock(sandbox).mock, { - logger: getLoggerMock(sandbox).mock, - }) + test('does not throw', async () => { + const service = await userV1Service.getInstance(getEventBridgeMock(sandbox).mock, { + logger: getLoggerMock(sandbox).mock, + }) - const ping = safeBind(pingCommandBuilder.getCommandFunction(), service) + const ping = safeBind(pingCommandBuilder.getCommandFunction(), service) - const payload: UserV1PingInputPayload = undefined + const payload: UserV1PingInputPayload = undefined - const parameter: UserV1PingInputParameter = {} + const parameter: UserV1PingInputParameter = {} - const context = pingCommandBuilder.getCommandContextMock(payload, parameter, sandbox) + const context = pingCommandBuilder.getCommandContextMock({ payload, parameter, sandbox }) - context.stubs.service.User[1].computeData.resolves('invoke response') + context.stubs.service.User[1].computeData.resolves('invoke response') - const result = await ping(context.mock, payload, parameter) + const result = await ping(context.mock, payload, parameter) - expect(result).toStrictEqual({ pong: 'invoke response' }) - }) + expect(result).toStrictEqual({ pong: 'invoke response' }) + }) }) diff --git a/examples/dapr-example/src/service/user/v1/command/ping/pingCommandBuilder.ts b/examples/dapr-example/src/service/user/v1/command/ping/pingCommandBuilder.ts index 4b440cd34..6b1f5c754 100644 --- a/examples/dapr-example/src/service/user/v1/command/ping/pingCommandBuilder.ts +++ b/examples/dapr-example/src/service/user/v1/command/ping/pingCommandBuilder.ts @@ -1,24 +1,24 @@ import { ServiceEvent } from '../../../../ServiceEvent.enum.js' import { userV1ServiceBuilder } from '../../userV1ServiceBuilder.js' import { - userV1PingInputParameterSchema, - userV1PingInputPayloadSchema, - userV1PingOutputPayloadSchema, + userV1PingInputParameterSchema, + userV1PingInputPayloadSchema, + userV1PingOutputPayloadSchema, } from './schema.js' export const pingCommandBuilder = userV1ServiceBuilder - .getCommandBuilder('ping', 'ping pong') - .setSuccessEventName(ServiceEvent.Pong) - .addPayloadSchema(userV1PingInputPayloadSchema) - .addParameterSchema(userV1PingInputParameterSchema) - .addOutputSchema(userV1PingOutputPayloadSchema) - .exposeAsHttpEndpoint('POST', 'ping', 'text/plain') - .canInvoke('User', '1', 'computeData') - .setCommandFunction(async function ({ service }, payload, _parameter) { - // add your business logic here - const pong = await service.User[1].computeData(payload, {}) + .getCommandBuilder('ping', 'ping pong') + .setSuccessEventName(ServiceEvent.Pong) + .addPayloadSchema(userV1PingInputPayloadSchema) + .addParameterSchema(userV1PingInputParameterSchema) + .addOutputSchema(userV1PingOutputPayloadSchema) + .exposeAsHttpEndpoint('POST', 'ping', 'text/plain') + .canInvoke('User', '1', 'computeData') + .setCommandFunction(async function ({ service }, payload, _parameter) { + // add your business logic here + const pong = await service.User[1].computeData(payload, {}) - return { - pong, - } - }) + return { + pong, + } + }) diff --git a/examples/dapr-example/src/service/user/v1/command/ping/schema.ts b/examples/dapr-example/src/service/user/v1/command/ping/schema.ts index 79542be9b..3b19dff75 100644 --- a/examples/dapr-example/src/service/user/v1/command/ping/schema.ts +++ b/examples/dapr-example/src/service/user/v1/command/ping/schema.ts @@ -9,8 +9,8 @@ export const userV1PingInputPayloadSchema = extendApi(z.any(), { title: 'ping in // define the output payload export const userV1PingOutputPayloadSchema = extendApi( - z.object({ - pong: z.any(), - }), - { title: 'ping output payload schema' }, + z.object({ + pong: z.any(), + }), + { title: 'ping output payload schema' }, ) diff --git a/examples/dapr-example/src/service/user/v1/command/ping/types.ts b/examples/dapr-example/src/service/user/v1/command/ping/types.ts index e536ebbf0..cdcfb6454 100644 --- a/examples/dapr-example/src/service/user/v1/command/ping/types.ts +++ b/examples/dapr-example/src/service/user/v1/command/ping/types.ts @@ -1,9 +1,9 @@ import type { z } from 'zod' import type { - userV1PingInputParameterSchema, - userV1PingInputPayloadSchema, - userV1PingOutputPayloadSchema, + userV1PingInputParameterSchema, + userV1PingInputPayloadSchema, + userV1PingOutputPayloadSchema, } from './schema.js' export type UserV1PingInputParameter = z.input diff --git a/examples/dapr-example/src/service/user/v1/command/signUp/schema.ts b/examples/dapr-example/src/service/user/v1/command/signUp/schema.ts index f969055f8..17d29b986 100644 --- a/examples/dapr-example/src/service/user/v1/command/signUp/schema.ts +++ b/examples/dapr-example/src/service/user/v1/command/signUp/schema.ts @@ -6,24 +6,24 @@ export const userV1SignUpInputParameterSchema = extendApi(z.object({}), { title: // define the input payload export const userV1SignUpInputPayloadSchema = extendApi( - z.object({ - email: extendApi(z.string().email(), { title: 'the email of the user to register', example: 'user@email.com' }), - name: extendApi(z.string().min(3), { - title: 'the name of the user to register', - example: 'User', - }), - password: extendApi(z.string().min(3), { - title: 'the name login password', - example: 'password', - }), - }), - { title: 'the sign up input' }, + z.object({ + email: extendApi(z.string().email(), { title: 'the email of the user to register', example: 'user@email.com' }), + name: extendApi(z.string().min(3), { + title: 'the name of the user to register', + example: 'User', + }), + password: extendApi(z.string().min(3), { + title: 'the name login password', + example: 'password', + }), + }), + { title: 'the sign up input' }, ) // define the output payload export const userV1SignUpOutputPayloadSchema = extendApi( - z.object({ - userId: extendApi(z.string().uuid(), { title: 'the user id', example: 'a5fef052-911c-472c-ac25-e2da327f0af5' }), - }), - { title: 'sign up output payload schema' }, + z.object({ + userId: extendApi(z.string().uuid(), { title: 'the user id', example: 'a5fef052-911c-472c-ac25-e2da327f0af5' }), + }), + { title: 'sign up output payload schema' }, ) diff --git a/examples/dapr-example/src/service/user/v1/command/signUp/signUp.test.ts b/examples/dapr-example/src/service/user/v1/command/signUp/signUp.test.ts index 5b4f31157..31bac83af 100644 --- a/examples/dapr-example/src/service/user/v1/command/signUp/signUp.test.ts +++ b/examples/dapr-example/src/service/user/v1/command/signUp/signUp.test.ts @@ -7,69 +7,69 @@ import { signUpCommandBuilder } from './signUpCommandBuilder.js' import type { UserV1SignUpInputParameter, UserV1SignUpInputPayload } from './types.js' describe('service User version 1 - command signUp', () => { - let sandbox = createSandbox() - beforeEach(() => { - sandbox = createSandbox() - }) + let sandbox = createSandbox() + beforeEach(() => { + sandbox = createSandbox() + }) - afterEach(() => { - sandbox.restore() - }) + afterEach(() => { + sandbox.restore() + }) - test('can register a new user', async () => { - const service = await userV1Service.getInstance(getEventBridgeMock(sandbox).mock, { - logger: getLoggerMock(sandbox).mock, - }) + test('can register a new user', async () => { + const service = await userV1Service.getInstance(getEventBridgeMock(sandbox).mock, { + logger: getLoggerMock(sandbox).mock, + }) - const signUp = safeBind(signUpCommandBuilder.getCommandFunction(), service) + const signUp = safeBind(signUpCommandBuilder.getCommandFunction(), service) - const payload: UserV1SignUpInputPayload = { - name: 'test user', - email: 'email@example.com', - password: 'password', - } + const payload: UserV1SignUpInputPayload = { + name: 'test user', + email: 'email@example.com', + password: 'password', + } - const parameter: UserV1SignUpInputParameter = {} + const parameter: UserV1SignUpInputParameter = {} - const context = signUpCommandBuilder.getCommandContextMock(payload, parameter, sandbox) + const context = signUpCommandBuilder.getCommandContextMock({ payload, parameter, sandbox }) - context.stubs.getState.resolves({}) - context.stubs.setState.resolves() + context.stubs.getState.resolves({}) + context.stubs.setState.resolves() - const result = await signUp(context.mock, payload, parameter) + const result = await signUp(context.mock, payload, parameter) - expect(result.userId).toBeDefined() - }) + expect(result.userId).toBeDefined() + }) - test('throws when a user with same email exist', async () => { - const service = await userV1Service.getInstance(getEventBridgeMock(sandbox).mock, { - logger: getLoggerMock(sandbox).mock, - }) + test('throws when a user with same email exist', async () => { + const service = await userV1Service.getInstance(getEventBridgeMock(sandbox).mock, { + logger: getLoggerMock(sandbox).mock, + }) - const signUp = safeBind(signUpCommandBuilder.getCommandFunction(), service) + const signUp = safeBind(signUpCommandBuilder.getCommandFunction(), service) - const payload: UserV1SignUpInputPayload = { - name: 'test user', - email: 'email@example.com', - password: 'password', - } + const payload: UserV1SignUpInputPayload = { + name: 'test user', + email: 'email@example.com', + password: 'password', + } - const parameter: UserV1SignUpInputParameter = {} + const parameter: UserV1SignUpInputParameter = {} - const context = signUpCommandBuilder.getCommandContextMock(payload, parameter, sandbox) + const context = signUpCommandBuilder.getCommandContextMock({ payload, parameter, sandbox }) - context.stubs.getState.resolves({ - [StateStoreKey.Users]: [ - { - name: 'test user', - email: 'email@example.com', - password: 'password', - userId: 'a5fef052-911c-472c-ac25-e2da327f0af5', - }, - ], - }) - context.stubs.setState.resolves() + context.stubs.getState.resolves({ + [StateStoreKey.Users]: [ + { + name: 'test user', + email: 'email@example.com', + password: 'password', + userId: 'a5fef052-911c-472c-ac25-e2da327f0af5', + }, + ], + }) + context.stubs.setState.resolves() - await expect(signUp(context.mock, payload, parameter)).rejects.toThrow('the user already exists') - }) + await expect(signUp(context.mock, payload, parameter)).rejects.toThrow('the user already exists') + }) }) diff --git a/examples/dapr-example/src/service/user/v1/command/signUp/signUpCommandBuilder.ts b/examples/dapr-example/src/service/user/v1/command/signUp/signUpCommandBuilder.ts index b44bbb80c..729c69670 100644 --- a/examples/dapr-example/src/service/user/v1/command/signUp/signUpCommandBuilder.ts +++ b/examples/dapr-example/src/service/user/v1/command/signUp/signUpCommandBuilder.ts @@ -7,37 +7,37 @@ import { StateStoreKey } from '../../../../../types/index.js' import { ServiceEvent } from '../../../../ServiceEvent.enum.js' import { userV1ServiceBuilder } from '../../userV1ServiceBuilder.js' import { - userV1SignUpInputParameterSchema, - userV1SignUpInputPayloadSchema, - userV1SignUpOutputPayloadSchema, + userV1SignUpInputParameterSchema, + userV1SignUpInputPayloadSchema, + userV1SignUpOutputPayloadSchema, } from './schema.js' export const signUpCommandBuilder = userV1ServiceBuilder - .getCommandBuilder('signUp', 'registers a new user at our product') - .setSuccessEventName(ServiceEvent.NewUserRegistered) - .addPayloadSchema(userV1SignUpInputPayloadSchema) - .addParameterSchema(userV1SignUpInputParameterSchema) - .addOutputSchema(userV1SignUpOutputPayloadSchema) - .exposeAsHttpEndpoint('POST', 'user/signup') - .setCommandFunction(async function (context, payload, _parameter) { - const result = (await context.states.getState(StateStoreKey.Users)) as { [StateStoreKey.Users]: User[] | undefined } - - if (result.users?.some((user) => user.email === payload.email)) { - throw new HandledError(StatusCode.BadRequest, 'the user already exists') - } - - const user: User = { - ...payload, - userId: randomUUID(), - } - - const users = result.users ?? [] - users.push(user) - - await context.states.setState(StateStoreKey.Users, users) - - // add your business logic here - context.logger.info('new user added') - - return user - }) + .getCommandBuilder('signUp', 'registers a new user at our product') + .setSuccessEventName(ServiceEvent.NewUserRegistered) + .addPayloadSchema(userV1SignUpInputPayloadSchema) + .addParameterSchema(userV1SignUpInputParameterSchema) + .addOutputSchema(userV1SignUpOutputPayloadSchema) + .exposeAsHttpEndpoint('POST', 'user/signup') + .setCommandFunction(async function (context, payload, _parameter) { + const result = (await context.states.getState(StateStoreKey.Users)) as { [StateStoreKey.Users]: User[] | undefined } + + if (result.users?.some(user => user.email === payload.email)) { + throw new HandledError(StatusCode.BadRequest, 'the user already exists') + } + + const user: User = { + ...payload, + userId: randomUUID(), + } + + const users = result.users ?? [] + users.push(user) + + await context.states.setState(StateStoreKey.Users, users) + + // add your business logic here + context.logger.info('new user added') + + return user + }) diff --git a/examples/dapr-example/src/service/user/v1/command/signUp/types.ts b/examples/dapr-example/src/service/user/v1/command/signUp/types.ts index 084667e07..a162fc0d0 100644 --- a/examples/dapr-example/src/service/user/v1/command/signUp/types.ts +++ b/examples/dapr-example/src/service/user/v1/command/signUp/types.ts @@ -1,9 +1,9 @@ import type { z } from 'zod' import type { - userV1SignUpInputParameterSchema, - userV1SignUpInputPayloadSchema, - userV1SignUpOutputPayloadSchema, + userV1SignUpInputParameterSchema, + userV1SignUpInputPayloadSchema, + userV1SignUpOutputPayloadSchema, } from './schema.js' export type UserV1SignUpInputParameter = z.input diff --git a/examples/dapr-example/src/service/user/v1/userV1Service.test.ts b/examples/dapr-example/src/service/user/v1/userV1Service.test.ts index 47603a4b9..7efe0970c 100644 --- a/examples/dapr-example/src/service/user/v1/userV1Service.test.ts +++ b/examples/dapr-example/src/service/user/v1/userV1Service.test.ts @@ -1,11 +1,7 @@ import { userV1Service as service } from './userV1Service.js' describe('service user version 1', () => { - it('has valid commands', () => { - service.validateCommandDefinitions() - }) - - it('has valid subscriptions', () => { - service.validateSubscriptionDefinitions() - }) + it('has valid configuration', () => { + service.testServiceSetup() + }) }) diff --git a/examples/dapr-example/src/service/user/v1/userV1Service.ts b/examples/dapr-example/src/service/user/v1/userV1Service.ts index 307d896f5..08b5d8d3f 100644 --- a/examples/dapr-example/src/service/user/v1/userV1Service.ts +++ b/examples/dapr-example/src/service/user/v1/userV1Service.ts @@ -12,15 +12,15 @@ import { userV1ServiceBuilder } from './userV1ServiceBuilder.js' // other service config should be done in ./userServiceBuilder.ts file const commandDefinitions: CommandDefinitionList = [ - signUpCommandBuilder.getDefinition(), - getUserByIdCommandBuilder.getDefinition(), - getAllUsersCommandBuilder.getDefinition(), - pingCommandBuilder.getDefinition(), - computeDataCommandBuilder.getDefinition(), + signUpCommandBuilder.getDefinition(), + getUserByIdCommandBuilder.getDefinition(), + getAllUsersCommandBuilder.getDefinition(), + pingCommandBuilder.getDefinition(), + computeDataCommandBuilder.getDefinition(), ] const subscriptionDefinitions: SubscriptionDefinitionList = [] export const userV1Service = userV1ServiceBuilder - .addCommandDefinition(...commandDefinitions) - .addSubscriptionDefinition(...subscriptionDefinitions) + .addCommandDefinition(...commandDefinitions) + .addSubscriptionDefinition(...subscriptionDefinitions) diff --git a/examples/dapr-example/src/service/user/v1/userV1ServiceBuilder.ts b/examples/dapr-example/src/service/user/v1/userV1ServiceBuilder.ts index b047cf552..57a76d32d 100644 --- a/examples/dapr-example/src/service/user/v1/userV1ServiceBuilder.ts +++ b/examples/dapr-example/src/service/user/v1/userV1ServiceBuilder.ts @@ -5,12 +5,10 @@ import { generalUserServiceInfo } from '../generalUserServiceInfo.js' import { userServiceV1ConfigSchema } from './userServiceConfig.js' export const userServiceInfo = { - serviceVersion: '1', - ...generalUserServiceInfo, + serviceVersion: '1', + ...generalUserServiceInfo, } as const satisfies ServiceInfoType // create a service builder instance and assign service config schema and default config. -export const userV1ServiceBuilder = new ServiceBuilder(userServiceInfo) - .setConfigSchema(userServiceV1ConfigSchema) - .setDefaultConfig({}) +export const userV1ServiceBuilder = new ServiceBuilder(userServiceInfo).setConfigSchema(userServiceV1ConfigSchema) diff --git a/examples/dapr-example/src/types/StateStoreKey.enum.ts b/examples/dapr-example/src/types/StateStoreKey.enum.ts index a25d9271c..7992eeae1 100644 --- a/examples/dapr-example/src/types/StateStoreKey.enum.ts +++ b/examples/dapr-example/src/types/StateStoreKey.enum.ts @@ -1,3 +1,3 @@ export enum StateStoreKey { - Users = 'users', + Users = 'users', } diff --git a/examples/dapr-example/src/types/User.ts b/examples/dapr-example/src/types/User.ts index 8e2616607..de6239fbf 100644 --- a/examples/dapr-example/src/types/User.ts +++ b/examples/dapr-example/src/types/User.ts @@ -1,6 +1,6 @@ export type User = { - email: string - name: string - password: string - userId: string + email: string + name: string + password: string + userId: string } diff --git a/examples/dapr-example/tsconfig.json b/examples/dapr-example/tsconfig.json index 6962530c7..c8de62e8b 100644 --- a/examples/dapr-example/tsconfig.json +++ b/examples/dapr-example/tsconfig.json @@ -1,11 +1,8 @@ { - "extends": "../../tsconfig.json", - - "compilerOptions": { - "outDir": "./build" - }, - "include": [ - "./src/index.ts", - "./test/*.ts", - ] -} \ No newline at end of file + "extends": "../../tsconfig.json", + + "compilerOptions": { + "outDir": "./build" + }, + "include": ["./src/index.ts", "./test/*.ts"] +} diff --git a/examples/fullexample/.nvmrc b/examples/fullexample/.nvmrc index 9a2a0e219..e55159821 100644 --- a/examples/fullexample/.nvmrc +++ b/examples/fullexample/.nvmrc @@ -1 +1 @@ -v20 +v20.15 diff --git a/examples/fullexample/package.json b/examples/fullexample/package.json index daa79c757..05dffd39b 100644 --- a/examples/fullexample/package.json +++ b/examples/fullexample/package.json @@ -1,62 +1,62 @@ { - "name": "@purista/full-example", - "version": "1.11.0", - "description": "purista backend framework", - "homepage": "https://purista.dev", - "private": true, - "repository": { - "type": "git", - "url": "git@github.com:sebastianwessel/purista.git" - }, - "author": "Sebastian Wessel", - "license": "ISC", - "type": "module", - "main": "src/index.ts", - "engines": { - "node": ">=18.15" - }, - "scripts": { - "start": "tsx src/index.ts | pino-pretty", - "grafana:start": "NODE_ENV=development tsx src/index.ts | pino-loki -pl traceId,spanId,serviceName,serviceVersion,serviceTarget --hostname http://localhost:3100", - "grafana:up": "docker compose -f grafana/docker-compose.yaml up --detach", - "grafana:down": "docker compose -f grafana/docker-compose.yaml down", - "jaeger:start": "NODE_ENV=development tsx src/index.ts | pino-pretty", - "jaeger:up": "docker compose -f jaeger/docker-compose.yaml up --detach", - "jaeger:down": "docker compose -f jaeger/docker-compose.yaml down", - "signoz:start": "NODE_ENV=development tsx src/index.ts | pino-pretty", - "signoz:up": "docker compose -f signoz/docker-compose.yaml up --detach", - "signoz:down": "docker compose -f signoz/docker-compose.yaml down", - "teletrace:start": "NODE_ENV=development tsx src/indexTeletrace | pino-pretty", - "teletrace:up": "docker compose -f teletrace/docker-compose.yaml up --detach", - "teletrace:down": "docker compose -f teletrace/docker-compose.yaml down", - "uptrace:start": "NODE_ENV=development tsx src/indexUptrace.ts | pino-pretty", - "uptrace:up": "docker compose -f uptrace/docker-compose.yaml up --detach", - "uptrace:down": "docker compose -f uptrace/docker-compose.yaml down", - "zipkin:start": "NODE_ENV=development tsx src/indexZipkin.ts | pino-pretty", - "zipkin:up": "DOCKER_TAG=develop docker compose -f zipkin/docker-compose.yaml up --detach", - "zipkin:down": "docker compose -f zipkin/docker-compose.yaml down", - "lint": "eslint . --ext .ts,.json --cache . --fix", - "test": "vitest" - }, - "devDependencies": { - "@types/node": "^20.11.17", - "sinon": "^17.0.1", - "tsx": "^4.7.0", - "typescript": "^5.3.3", - "vitest": "^1.3.0" - }, - "dependencies": { - "@fastify/static": "^7.0.1", - "@opentelemetry/exporter-trace-otlp-http": "^0.48.0", - "@opentelemetry/exporter-zipkin": "^1.19.0", - "@opentelemetry/sdk-trace-node": "^1.19.0", - "@purista/amqpbridge": "*", - "@purista/core": "*", - "@purista/httpserver": "*", - "@purista/redis-state-store": "*", - "@uptrace/node": "^1.19.0", - "pino-loki": "^2.2.1", - "pino-pretty": "^10.3.1", - "zod": "^3.22.4" - } + "name": "@purista/full-example", + "version": "1.11.0", + "description": "purista backend framework", + "homepage": "https://purista.dev", + "private": true, + "repository": { + "type": "git", + "url": "git@github.com:puristajs/purista.git" + }, + "author": "Sebastian Wessel", + "license": "ISC", + "type": "module", + "main": "src/index.ts", + "engines": { + "node": ">=18.15" + }, + "scripts": { + "start": "tsx src/index.ts | pino-pretty", + "grafana:start": "NODE_ENV=development tsx src/index.ts | pino-loki -pl traceId,spanId,serviceName,serviceVersion,serviceTarget --hostname http://localhost:3100", + "grafana:up": "docker compose -f grafana/docker-compose.yaml up --detach", + "grafana:down": "docker compose -f grafana/docker-compose.yaml down", + "jaeger:start": "NODE_ENV=development tsx src/index.ts | pino-pretty", + "jaeger:up": "docker compose -f jaeger/docker-compose.yaml up --detach", + "jaeger:down": "docker compose -f jaeger/docker-compose.yaml down", + "signoz:start": "NODE_ENV=development tsx src/index.ts | pino-pretty", + "signoz:up": "docker compose -f signoz/docker-compose.yaml up --detach", + "signoz:down": "docker compose -f signoz/docker-compose.yaml down", + "teletrace:start": "NODE_ENV=development tsx src/indexTeletrace | pino-pretty", + "teletrace:up": "docker compose -f teletrace/docker-compose.yaml up --detach", + "teletrace:down": "docker compose -f teletrace/docker-compose.yaml down", + "uptrace:start": "NODE_ENV=development tsx src/indexUptrace.ts | pino-pretty", + "uptrace:up": "docker compose -f uptrace/docker-compose.yaml up --detach", + "uptrace:down": "docker compose -f uptrace/docker-compose.yaml down", + "zipkin:start": "NODE_ENV=development tsx src/indexZipkin.ts | pino-pretty", + "zipkin:up": "DOCKER_TAG=develop docker compose -f zipkin/docker-compose.yaml up --detach", + "zipkin:down": "docker compose -f zipkin/docker-compose.yaml down", + "lint": "npx @biomejs/biome check --write", + "test": "vitest" + }, + "devDependencies": { + "@types/node": "^22.5.1", + "sinon": "^19.0.2", + "tsx": "^4.19.0", + "typescript": "^5.5.4", + "vitest": "^3.0.4" + }, + "dependencies": { + "@fastify/static": "^8.0.3", + "@opentelemetry/exporter-trace-otlp-http": "^0.57.1", + "@opentelemetry/exporter-zipkin": "^1.25.1", + "@opentelemetry/sdk-trace-node": "^1.26.0", + "@purista/amqpbridge": "*", + "@purista/core": "*", + "@purista/httpserver": "*", + "@purista/redis-state-store": "*", + "@uptrace/node": "^1.21.0", + "pino-loki": "^2.3.0", + "pino-pretty": "^13.0.0", + "zod": "^3.24.1" + } } diff --git a/examples/fullexample/src/config/httpServerConfig.ts b/examples/fullexample/src/config/httpServerConfig.ts index 4ad70a945..89808c616 100644 --- a/examples/fullexample/src/config/httpServerConfig.ts +++ b/examples/fullexample/src/config/httpServerConfig.ts @@ -1,20 +1,20 @@ import type { HttpServerServiceV1Config } from '@purista/httpserver' const httpServerConfig: HttpServerServiceV1Config = { - fastify: {}, - port: 8080, - logLevel: 'debug', - domain: 'localhost', - apiMountPath: '/api', - enableCors: false, - openApi: { - enabled: true, - info: { - title: 'backend api', - description: 'OpenApi definition for server endpoints', - version: '1.0.0', - }, - }, + fastify: {}, + port: 8080, + logLevel: 'debug', + domain: 'localhost', + apiMountPath: '/api', + enableCors: false, + openApi: { + enabled: true, + info: { + title: 'backend api', + description: 'OpenApi definition for server endpoints', + version: '1.0.0', + }, + }, } export default httpServerConfig diff --git a/examples/fullexample/src/config/jaegerConfig.ts b/examples/fullexample/src/config/jaegerConfig.ts index d2bc6791c..a62e70763 100644 --- a/examples/fullexample/src/config/jaegerConfig.ts +++ b/examples/fullexample/src/config/jaegerConfig.ts @@ -1,3 +1,3 @@ export const jaegerExporterOptions = { - url: `http://localhost:4318/v1/traces`, + url: 'http://localhost:4318/v1/traces', } diff --git a/examples/fullexample/src/config/teletrace.ts b/examples/fullexample/src/config/teletrace.ts index f22a09a1a..0d39aa608 100644 --- a/examples/fullexample/src/config/teletrace.ts +++ b/examples/fullexample/src/config/teletrace.ts @@ -1,3 +1,3 @@ export const teletraceExporterOptions = { - url: `http://localhost:4318/v1/traces`, + url: 'http://localhost:4318/v1/traces', } diff --git a/examples/fullexample/src/config/uptraceConfig.ts b/examples/fullexample/src/config/uptraceConfig.ts index da7b4d0d4..fea3d07b3 100644 --- a/examples/fullexample/src/config/uptraceConfig.ts +++ b/examples/fullexample/src/config/uptraceConfig.ts @@ -1,4 +1,4 @@ export const uptraceConfig = { - url: `http://purista_secret_token@localhost:14318/v1/traces`, - headers: { 'uptrace-dsn': 'http://purista_secret_token@localhost:14318/3' }, + url: 'http://purista_secret_token@localhost:14318/v1/traces', + headers: { 'uptrace-dsn': 'http://purista_secret_token@localhost:14318/3' }, } diff --git a/examples/fullexample/src/config/zipkinConfig.ts b/examples/fullexample/src/config/zipkinConfig.ts index b80b992be..c49c0a4f1 100644 --- a/examples/fullexample/src/config/zipkinConfig.ts +++ b/examples/fullexample/src/config/zipkinConfig.ts @@ -1,3 +1,3 @@ export const zipkinExporterOptions = { - url: `http://localhost:9411/api/v2/spans`, + url: 'http://localhost:9411/api/v2/spans', } diff --git a/examples/fullexample/src/getProcessor.ts b/examples/fullexample/src/getProcessor.ts index cfb0f49b6..22a60f9a2 100644 --- a/examples/fullexample/src/getProcessor.ts +++ b/examples/fullexample/src/getProcessor.ts @@ -12,31 +12,31 @@ import { zipkinExporterOptions } from './config/zipkinConfig.js' * @returns OpenTelemetry exporter instance */ export const getJaegerExporter = () => { - const exporter = new OTLPTraceExporter(jaegerExporterOptions) + const exporter = new OTLPTraceExporter(jaegerExporterOptions) - return new SimpleSpanProcessor(exporter) + return new SimpleSpanProcessor(exporter) } export const getTeletraceExporter = () => { - const exporter = new OTLPTraceExporter(teletraceExporterOptions) + const exporter = new OTLPTraceExporter(teletraceExporterOptions) - return new SimpleSpanProcessor(exporter) + return new SimpleSpanProcessor(exporter) } export const getZipkinExporter = () => { - const exporter = new ZipkinExporter(zipkinExporterOptions) + const exporter = new ZipkinExporter(zipkinExporterOptions) - return new SimpleSpanProcessor(exporter) + return new SimpleSpanProcessor(exporter) } export const getConsoleSpanExporter = () => { - const exporter = new ConsoleSpanExporter() + const exporter = new ConsoleSpanExporter() - return new SimpleSpanProcessor(exporter) + return new SimpleSpanProcessor(exporter) } export const uptraceTraceExporter = () => { - const exporter = new OTLPTraceExporter(uptraceConfig) + const exporter = new OTLPTraceExporter(uptraceConfig) - return new SimpleSpanProcessor(exporter) + return new SimpleSpanProcessor(exporter) } diff --git a/examples/fullexample/src/indexUptrace.ts b/examples/fullexample/src/indexUptrace.ts index b75453be2..0ceaa5e35 100644 --- a/examples/fullexample/src/indexUptrace.ts +++ b/examples/fullexample/src/indexUptrace.ts @@ -2,7 +2,7 @@ import { uptraceTraceExporter } from './getProcessor.js' import { main } from './main.js' const start = async () => { - main(uptraceTraceExporter) + main(uptraceTraceExporter) } start() diff --git a/examples/fullexample/src/main.ts b/examples/fullexample/src/main.ts index 12e5c2a94..a887e414a 100644 --- a/examples/fullexample/src/main.ts +++ b/examples/fullexample/src/main.ts @@ -12,82 +12,82 @@ import { emailV1Service } from './service/email/v1/index.js' import { userV1Service } from './service/user/v1/index.js' export const main = async (getProcessor: () => SpanProcessor) => { - // initialize the logging - const logger = initLogger() + // initialize the logging + const logger = initLogger() - logger.info('application starts') + logger.info('application starts') - const spanProcessor = getProcessor() + const spanProcessor = getProcessor() - // create and init our eventbridge - const eventBridge = new AmqpBridge({ - spanProcessor, - }) - await eventBridge.start() + // create and init our eventbridge + const eventBridge = new AmqpBridge({ + spanProcessor, + }) + await eventBridge.start() - // create and init a webserver - const httpServerService = await httpServerV1Service.getInstance(eventBridge, { - serviceConfig: httpServerConfig, - spanProcessor, - }) + // create and init a webserver + const httpServerService = await httpServerV1Service.getInstance(eventBridge, { + serviceConfig: httpServerConfig, + spanProcessor, + }) - const defaultPublicPath = resolve(__dirname, '..', 'public') + const defaultPublicPath = resolve(__dirname, '..', 'public') - // static file handler - httpServerService.server?.register(fastifyStatic, { - root: defaultPublicPath, - decorateReply: false, - }) + // static file handler + httpServerService.server?.register(fastifyStatic, { + root: defaultPublicPath, + decorateReply: false, + }) - // start the webserver - await httpServerService.start() + // start the webserver + await httpServerService.start() - // create a state store - const stateStore = new RedisStateStore({ config: { url: 'redis://localhost:6379' } }) - // create config store - const configStore = new DefaultConfigStore({ - config: { - emailProviderUrl: 'https://example.com', - }, - }) - // create secret store - const secretStore = new DefaultSecretStore({ - config: { - emailProviderAuthToken: 'some-secret-token', - }, - }) + // create a state store + const stateStore = new RedisStateStore({ config: { url: 'redis://localhost:6379' } }) + // create config store + const configStore = new DefaultConfigStore({ + config: { + emailProviderUrl: 'https://example.com', + }, + }) + // create secret store + const secretStore = new DefaultSecretStore({ + config: { + emailProviderAuthToken: 'some-secret-token', + }, + }) - const userService = await userV1Service.getInstance(eventBridge, { - spanProcessor, - stateStore, - configStore, - secretStore, - }) - await userService.start() + const userService = await userV1Service.getInstance(eventBridge, { + spanProcessor, + stateStore, + configStore, + secretStore, + }) + await userService.start() - const emailService = await emailV1Service.getInstance(eventBridge, { - spanProcessor, - stateStore, - configStore, - secretStore, - }) - await emailService.start() + const emailService = await emailV1Service.getInstance(eventBridge, { + spanProcessor, + stateStore, + configStore, + secretStore, + }) + await emailService.start() - logger.info('application ready') - logger.info(`open in browser: http://localhost:${httpServerConfig.port}`) + logger.info('application ready') + logger.info(`open in browser: http://localhost:${httpServerConfig.port}`) - gracefulShutdown(logger, [ - // begin with the event bridge to no longer accept incoming messages - eventBridge, - userService, - emailService, - httpServerService, - secretStore, - stateStore, - configStore, - { - name: 'OTSpanProcessor', - destroy: () => spanProcessor.shutdown(), - }, - ]) + gracefulShutdown(logger, [ + // begin with the event bridge to no longer accept incoming messages + eventBridge, + userService, + emailService, + httpServerService, + secretStore, + stateStore, + configStore, + { + name: 'OTSpanProcessor', + destroy: () => spanProcessor.shutdown(), + }, + ]) } diff --git a/examples/fullexample/src/service/ServiceEvent.enum.ts b/examples/fullexample/src/service/ServiceEvent.enum.ts index 5673fb208..a9103a39b 100644 --- a/examples/fullexample/src/service/ServiceEvent.enum.ts +++ b/examples/fullexample/src/service/ServiceEvent.enum.ts @@ -1,12 +1,12 @@ export enum ServiceEvent { - /** - * Emitted by user v1 command signUp: - * a new user registration - */ - NewUserRegistered = 'newUserRegistered', - /** - * Emitted by email v1 subscription sendWelcomeEmail: - * a new user registration - */ - WelcomeEmailSent = 'send a welcome mail to new registered users', + /** + * Emitted by user v1 command signUp: + * a new user registration + */ + NewUserRegistered = 'newUserRegistered', + /** + * Emitted by email v1 subscription sendWelcomeEmail: + * a new user registration + */ + WelcomeEmailSent = 'send a welcome mail to new registered users', } diff --git a/examples/fullexample/src/service/email/generalEmailServiceInfo.ts b/examples/fullexample/src/service/email/generalEmailServiceInfo.ts index c8cbc5538..446d54e53 100644 --- a/examples/fullexample/src/service/email/generalEmailServiceInfo.ts +++ b/examples/fullexample/src/service/email/generalEmailServiceInfo.ts @@ -1,6 +1,6 @@ import type { ServiceInfoType } from '@purista/core' export const generalEmailServiceInfo = { - serviceName: 'Email', - serviceDescription: 'sends emails to customers', + serviceName: 'Email', + serviceDescription: 'sends emails to customers', } as const satisfies Omit diff --git a/examples/fullexample/src/service/email/v1/emailV1Service.test.ts b/examples/fullexample/src/service/email/v1/emailV1Service.test.ts index 5b9b12e07..7729e529c 100644 --- a/examples/fullexample/src/service/email/v1/emailV1Service.test.ts +++ b/examples/fullexample/src/service/email/v1/emailV1Service.test.ts @@ -1,11 +1,7 @@ import { emailV1Service as service } from './emailV1Service.js' describe('service email version 1', () => { - it('has valid commands', () => { - service.validateCommandDefinitions() - }) - - it('has valid subscriptions', () => { - service.validateSubscriptionDefinitions() - }) + it('has valid configuration', () => { + service.testServiceSetup() + }) }) diff --git a/examples/fullexample/src/service/email/v1/emailV1Service.ts b/examples/fullexample/src/service/email/v1/emailV1Service.ts index 6b8cdf599..8535f9ba3 100644 --- a/examples/fullexample/src/service/email/v1/emailV1Service.ts +++ b/examples/fullexample/src/service/email/v1/emailV1Service.ts @@ -12,5 +12,5 @@ const commandDefinitions: CommandDefinitionList = [] const subscriptionDefinitions: SubscriptionDefinitionList = [sendWelcomeEmailSubscriptionBuilder.getDefinition()] export const emailV1Service = emailV1ServiceBuilder - .addCommandDefinition(...commandDefinitions) - .addSubscriptionDefinition(...subscriptionDefinitions) + .addCommandDefinition(...commandDefinitions) + .addSubscriptionDefinition(...subscriptionDefinitions) diff --git a/examples/fullexample/src/service/email/v1/emailV1ServiceBuilder.ts b/examples/fullexample/src/service/email/v1/emailV1ServiceBuilder.ts index ab2fd3b9a..926b0389e 100644 --- a/examples/fullexample/src/service/email/v1/emailV1ServiceBuilder.ts +++ b/examples/fullexample/src/service/email/v1/emailV1ServiceBuilder.ts @@ -5,12 +5,10 @@ import { generalEmailServiceInfo } from '../generalEmailServiceInfo.js' import { emailServiceV1ConfigSchema } from './emailServiceConfig.js' export const emailServiceInfo = { - serviceVersion: '1', - ...generalEmailServiceInfo, + serviceVersion: '1', + ...generalEmailServiceInfo, } as const satisfies ServiceInfoType // create a service builder instance and assign service config schema and default config. -export const emailV1ServiceBuilder = new ServiceBuilder(emailServiceInfo) - .setConfigSchema(emailServiceV1ConfigSchema) - .setDefaultConfig({}) +export const emailV1ServiceBuilder = new ServiceBuilder(emailServiceInfo).setConfigSchema(emailServiceV1ConfigSchema) diff --git a/examples/fullexample/src/service/email/v1/subscription/sendWelcomeEmail/schema.ts b/examples/fullexample/src/service/email/v1/subscription/sendWelcomeEmail/schema.ts index 97bd10475..227f140eb 100644 --- a/examples/fullexample/src/service/email/v1/subscription/sendWelcomeEmail/schema.ts +++ b/examples/fullexample/src/service/email/v1/subscription/sendWelcomeEmail/schema.ts @@ -2,9 +2,9 @@ import { z } from 'zod' // define the input payload export const emailV1SendWelcomeEmailInputPayloadSchema = z.object({ - userId: z.string().uuid(), + userId: z.string().uuid(), }) export const emailV1SendWelcomeEmailOutputPayloadSchema = z.object({ - userId: z.string().uuid(), + userId: z.string().uuid(), }) diff --git a/examples/fullexample/src/service/email/v1/subscription/sendWelcomeEmail/sendWelcomeEmail.test.ts b/examples/fullexample/src/service/email/v1/subscription/sendWelcomeEmail/sendWelcomeEmail.test.ts index 1e0f8fb27..20ad177a5 100644 --- a/examples/fullexample/src/service/email/v1/subscription/sendWelcomeEmail/sendWelcomeEmail.test.ts +++ b/examples/fullexample/src/service/email/v1/subscription/sendWelcomeEmail/sendWelcomeEmail.test.ts @@ -7,54 +7,54 @@ import { sendWelcomeEmailSubscriptionBuilder } from './sendWelcomeEmailSubscript import type { EmailV1SendWelcomeEmailInputPayload } from './types.js' describe('service Email version 1 - subscription sendWelcomeEmail', () => { - let sandbox = createSandbox() - beforeEach(() => { - sandbox = createSandbox() - }) - - afterEach(() => { - sandbox.restore() - }) - - test('sends an email', async () => { - // create a service instance to be bind to the subscription function - const service = await emailV1Service.getInstance(getEventBridgeMock(sandbox).mock, { - logger: getLoggerMock(sandbox).mock, - }) - - // get the subscription function and bind to service instance to work properly - const sendWelcomeEmail = safeBind(sendWelcomeEmailSubscriptionBuilder.getSubscriptionFunction(), service) - - const userMock: User = { - email: 'email@example.com', - name: 'test user', - password: 'password', - userId: 'a5fef052-911c-472c-ac25-e2da327f0af5', - } - - // define the test input payload - const payload: EmailV1SendWelcomeEmailInputPayload = { userId: userMock.userId } - - // define the test input parameter - const parameter = undefined as unknown as Readonly - - // create a mock message with the expected input for the subscription function - const message = getCommandSuccessMessageMock(payload) - - // create a subscription context for the subscription function - const context = sendWelcomeEmailSubscriptionBuilder.getSubscriptionContextMock(message, sandbox) - - context.stubs.service.User['1'].getUserById.resolves(userMock) - context.stubs.getConfig.resolves({ emailProviderUrl: 'https://example.com' }) - context.stubs.getSecret.resolves({ emailProviderAuthToken: 'secret_token' }) - - // execute the subscription function - const result = await sendWelcomeEmail(context.mock, payload, parameter) - - expect( - context.stubs.logger.debug.calledWith('Using email provider https://example.com with token secret_token'), - ).toBeTruthy() - expect(context.stubs.logger.info.calledWith('Welcome email to user sent to ' + userMock.email)).toBeTruthy() - expect(result).toStrictEqual({ userId: userMock.userId }) - }) + let sandbox = createSandbox() + beforeEach(() => { + sandbox = createSandbox() + }) + + afterEach(() => { + sandbox.restore() + }) + + test('sends an email', async () => { + // create a service instance to be bind to the subscription function + const service = await emailV1Service.getInstance(getEventBridgeMock(sandbox).mock, { + logger: getLoggerMock(sandbox).mock, + }) + + // get the subscription function and bind to service instance to work properly + const sendWelcomeEmail = safeBind(sendWelcomeEmailSubscriptionBuilder.getSubscriptionFunction(), service) + + const userMock: User = { + email: 'email@example.com', + name: 'test user', + password: 'password', + userId: 'a5fef052-911c-472c-ac25-e2da327f0af5', + } + + // define the test input payload + const payload: EmailV1SendWelcomeEmailInputPayload = { userId: userMock.userId } + + // define the test input parameter + const parameter = undefined as unknown as Readonly + + // create a mock message with the expected input for the subscription function + const message = getCommandSuccessMessageMock(payload) + + // create a subscription context for the subscription function + const context = sendWelcomeEmailSubscriptionBuilder.getSubscriptionContextMock({ message, sandbox }) + + context.stubs.service.User['1'].getUserById.resolves(userMock) + context.stubs.getConfig.resolves({ emailProviderUrl: 'https://example.com' }) + context.stubs.getSecret.resolves({ emailProviderAuthToken: 'secret_token' }) + + // execute the subscription function + const result = await sendWelcomeEmail(context.mock, payload, parameter) + + expect( + context.stubs.logger.debug.calledWith('Using email provider https://example.com with token secret_token'), + ).toBeTruthy() + expect(context.stubs.logger.info.calledWith(`Welcome email to user sent to ${userMock.email}`)).toBeTruthy() + expect(result).toStrictEqual({ userId: userMock.userId }) + }) }) diff --git a/examples/fullexample/src/service/email/v1/subscription/sendWelcomeEmail/sendWelcomeEmailSubscriptionBuilder.ts b/examples/fullexample/src/service/email/v1/subscription/sendWelcomeEmail/sendWelcomeEmailSubscriptionBuilder.ts index 59001104e..58c99facc 100644 --- a/examples/fullexample/src/service/email/v1/subscription/sendWelcomeEmail/sendWelcomeEmailSubscriptionBuilder.ts +++ b/examples/fullexample/src/service/email/v1/subscription/sendWelcomeEmail/sendWelcomeEmailSubscriptionBuilder.ts @@ -1,35 +1,35 @@ import { ServiceEvent } from '../../../../ServiceEvent.enum.js' import { - userV1GetUserByIdInputParameterSchema, - userV1GetUserByIdInputPayloadSchema, - userV1GetUserByIdOutputPayloadSchema, + userV1GetUserByIdInputParameterSchema, + userV1GetUserByIdInputPayloadSchema, + userV1GetUserByIdOutputPayloadSchema, } from '../../../../user/v1/command/getUserById/schema.js' import { emailV1ServiceBuilder } from '../../emailV1ServiceBuilder.js' import { emailV1SendWelcomeEmailInputPayloadSchema, emailV1SendWelcomeEmailOutputPayloadSchema } from './schema.js' export const sendWelcomeEmailSubscriptionBuilder = emailV1ServiceBuilder - .getSubscriptionBuilder('sendWelcomeEmail', 'send a welcome mail to new registered users') - .subscribeToEvent(ServiceEvent.NewUserRegistered) - .addPayloadSchema(emailV1SendWelcomeEmailInputPayloadSchema) - .addOutputSchema(ServiceEvent.WelcomeEmailSent, emailV1SendWelcomeEmailOutputPayloadSchema) - .canInvoke( - 'User', - '1', - 'getUserById', - userV1GetUserByIdOutputPayloadSchema, - userV1GetUserByIdInputPayloadSchema, - userV1GetUserByIdInputParameterSchema, - ) - .setSubscriptionFunction(async function (context, payload, _parameter) { - const config = await context.configs.getConfig('emailProviderUrl') - const secrets = await context.secrets.getSecret('emailProviderAuthToken') + .getSubscriptionBuilder('sendWelcomeEmail', 'send a welcome mail to new registered users') + .subscribeToEvent(ServiceEvent.NewUserRegistered) + .addPayloadSchema(emailV1SendWelcomeEmailInputPayloadSchema) + .addOutputSchema(ServiceEvent.WelcomeEmailSent, emailV1SendWelcomeEmailOutputPayloadSchema) + .canInvoke( + 'User', + '1', + 'getUserById', + userV1GetUserByIdOutputPayloadSchema, + userV1GetUserByIdInputPayloadSchema, + userV1GetUserByIdInputParameterSchema, + ) + .setSubscriptionFunction(async function (context, payload, _parameter) { + const config = await context.configs.getConfig('emailProviderUrl') + const secrets = await context.secrets.getSecret('emailProviderAuthToken') - context.logger.debug(`Using email provider ${config.emailProviderUrl} with token ${secrets.emailProviderAuthToken}`) + context.logger.debug(`Using email provider ${config.emailProviderUrl} with token ${secrets.emailProviderAuthToken}`) - const user = await context.service.User['1'].getUserById(undefined, payload) + const user = await context.service.User['1'].getUserById(undefined, payload) - // add your business logic here - context.logger.info(`Welcome email to user sent to ${user.email}`) + // add your business logic here + context.logger.info(`Welcome email to user sent to ${user.email}`) - return payload - }) + return payload + }) diff --git a/examples/fullexample/src/service/user/generalUserServiceInfo.ts b/examples/fullexample/src/service/user/generalUserServiceInfo.ts index b5e2bafd7..770f5c84e 100644 --- a/examples/fullexample/src/service/user/generalUserServiceInfo.ts +++ b/examples/fullexample/src/service/user/generalUserServiceInfo.ts @@ -1,6 +1,6 @@ import type { ServiceInfoType } from '@purista/core' export const generalUserServiceInfo = { - serviceName: 'User', - serviceDescription: 'manage user information', + serviceName: 'User', + serviceDescription: 'manage user information', } as const satisfies Omit diff --git a/examples/fullexample/src/service/user/v1/command/getAllUsers/getAllUsers.test.ts b/examples/fullexample/src/service/user/v1/command/getAllUsers/getAllUsers.test.ts index 3917b01dd..2e1b8032e 100644 --- a/examples/fullexample/src/service/user/v1/command/getAllUsers/getAllUsers.test.ts +++ b/examples/fullexample/src/service/user/v1/command/getAllUsers/getAllUsers.test.ts @@ -8,41 +8,41 @@ import { getAllUsersCommandBuilder } from './getAllUsersCommandBuilder.js' import type { UserV1GetAllUsersInputParameter, UserV1GetAllUsersInputPayload } from './types.js' describe('service User version 1 - command getAllUsers', () => { - let sandbox = createSandbox() - beforeEach(() => { - sandbox = createSandbox() - }) + let sandbox = createSandbox() + beforeEach(() => { + sandbox = createSandbox() + }) - afterEach(() => { - sandbox.restore() - }) + afterEach(() => { + sandbox.restore() + }) - test('does not throw', async () => { - const service = await userV1Service.getInstance(getEventBridgeMock(sandbox).mock, { - logger: getLoggerMock(sandbox).mock, - }) + test('does not throw', async () => { + const service = await userV1Service.getInstance(getEventBridgeMock(sandbox).mock, { + logger: getLoggerMock(sandbox).mock, + }) - const getAllUsers = safeBind(getAllUsersCommandBuilder.getCommandFunction(), service) + const getAllUsers = safeBind(getAllUsersCommandBuilder.getCommandFunction(), service) - const payload: UserV1GetAllUsersInputPayload = undefined + const payload: UserV1GetAllUsersInputPayload = undefined - const parameter: UserV1GetAllUsersInputParameter = {} + const parameter: UserV1GetAllUsersInputParameter = {} - const context = getAllUsersCommandBuilder.getCommandContextMock(payload, parameter, sandbox) + const context = getAllUsersCommandBuilder.getCommandContextMock({ payload, parameter, sandbox }) - const userMock: User = { - email: 'email@example.com', - name: 'test user', - password: 'password', - userId: 'a5fef052-911c-472c-ac25-e2da327f0af5', - } + const userMock: User = { + email: 'email@example.com', + name: 'test user', + password: 'password', + userId: 'a5fef052-911c-472c-ac25-e2da327f0af5', + } - context.stubs.getState.resolves({ - [StateStoreKey.Users]: [userMock], - }) + context.stubs.getState.resolves({ + [StateStoreKey.Users]: [userMock], + }) - const result = await getAllUsers(context.mock, payload, parameter) + const result = await getAllUsers(context.mock, payload, parameter) - expect(result).toStrictEqual([{ name: userMock.name, email: userMock.email, userId: userMock.userId }]) - }) + expect(result).toStrictEqual([{ name: userMock.name, email: userMock.email, userId: userMock.userId }]) + }) }) diff --git a/examples/fullexample/src/service/user/v1/command/getAllUsers/getAllUsersCommandBuilder.ts b/examples/fullexample/src/service/user/v1/command/getAllUsers/getAllUsersCommandBuilder.ts index 482dda298..911cde6bf 100644 --- a/examples/fullexample/src/service/user/v1/command/getAllUsers/getAllUsersCommandBuilder.ts +++ b/examples/fullexample/src/service/user/v1/command/getAllUsers/getAllUsersCommandBuilder.ts @@ -2,20 +2,20 @@ import type { User } from '../../../../../types/index.js' import { StateStoreKey } from '../../../../../types/index.js' import { userV1ServiceBuilder } from '../../userV1ServiceBuilder.js' import { - userV1GetAllUsersInputParameterSchema, - userV1GetAllUsersInputPayloadSchema, - userV1GetAllUsersOutputPayloadSchema, + userV1GetAllUsersInputParameterSchema, + userV1GetAllUsersInputPayloadSchema, + userV1GetAllUsersOutputPayloadSchema, } from './schema.js' export const getAllUsersCommandBuilder = userV1ServiceBuilder - .getCommandBuilder('getAllUsers', 'returns a list of registered users') - .addPayloadSchema(userV1GetAllUsersInputPayloadSchema) - .addParameterSchema(userV1GetAllUsersInputParameterSchema) - .addOutputSchema(userV1GetAllUsersOutputPayloadSchema) - .exposeAsHttpEndpoint('GET', '/user') - .setCommandFunction(async function (context, _payload, _parameter) { - const result = (await context.states.getState(StateStoreKey.Users)) as { [StateStoreKey.Users]: User[] | undefined } - const users = result.users ?? [] + .getCommandBuilder('getAllUsers', 'returns a list of registered users') + .addPayloadSchema(userV1GetAllUsersInputPayloadSchema) + .addParameterSchema(userV1GetAllUsersInputParameterSchema) + .addOutputSchema(userV1GetAllUsersOutputPayloadSchema) + .exposeAsHttpEndpoint('GET', '/user') + .setCommandFunction(async function (context, _payload, _parameter) { + const result = (await context.states.getState(StateStoreKey.Users)) as { [StateStoreKey.Users]: User[] | undefined } + const users = result.users ?? [] - return users - }) + return users + }) diff --git a/examples/fullexample/src/service/user/v1/command/getAllUsers/schema.ts b/examples/fullexample/src/service/user/v1/command/getAllUsers/schema.ts index 32c736fc2..91a45623c 100644 --- a/examples/fullexample/src/service/user/v1/command/getAllUsers/schema.ts +++ b/examples/fullexample/src/service/user/v1/command/getAllUsers/schema.ts @@ -3,22 +3,22 @@ import { z } from 'zod' // define the input parameters export const userV1GetAllUsersInputParameterSchema = extendApi(z.object({}), { - title: 'get all users input parameter schema', + title: 'get all users input parameter schema', }) // define the input payload export const userV1GetAllUsersInputPayloadSchema = z.undefined() export const userV1GetAllUsersUserEntrySchema = z.object({ - userId: extendApi(z.string().uuid(), { title: 'the user id', example: 'a5fef052-911c-472c-ac25-e2da327f0af5' }), - email: extendApi(z.string().email(), { title: 'the email of the user to register', example: 'user@email.com' }), - name: extendApi(z.string().min(3), { - title: 'the name of the user to register', - example: 'User', - }), + userId: extendApi(z.string().uuid(), { title: 'the user id', example: 'a5fef052-911c-472c-ac25-e2da327f0af5' }), + email: extendApi(z.string().email(), { title: 'the email of the user to register', example: 'user@email.com' }), + name: extendApi(z.string().min(3), { + title: 'the name of the user to register', + example: 'User', + }), }) // define the output payload export const userV1GetAllUsersOutputPayloadSchema = extendApi(z.array(userV1GetAllUsersUserEntrySchema), { - title: 'get all users output payload schema', + title: 'get all users output payload schema', }) diff --git a/examples/fullexample/src/service/user/v1/command/getAllUsers/types.ts b/examples/fullexample/src/service/user/v1/command/getAllUsers/types.ts index c8486af0d..4e88b232e 100644 --- a/examples/fullexample/src/service/user/v1/command/getAllUsers/types.ts +++ b/examples/fullexample/src/service/user/v1/command/getAllUsers/types.ts @@ -1,9 +1,9 @@ import type { z } from 'zod' import type { - userV1GetAllUsersInputParameterSchema, - userV1GetAllUsersInputPayloadSchema, - userV1GetAllUsersOutputPayloadSchema, + userV1GetAllUsersInputParameterSchema, + userV1GetAllUsersInputPayloadSchema, + userV1GetAllUsersOutputPayloadSchema, } from './schema.js' export type UserV1GetAllUsersInputParameter = z.input diff --git a/examples/fullexample/src/service/user/v1/command/getUserById/getUserById.test.ts b/examples/fullexample/src/service/user/v1/command/getUserById/getUserById.test.ts index ef9664911..b9bd14727 100644 --- a/examples/fullexample/src/service/user/v1/command/getUserById/getUserById.test.ts +++ b/examples/fullexample/src/service/user/v1/command/getUserById/getUserById.test.ts @@ -8,68 +8,68 @@ import { getUserByIdCommandBuilder } from './getUserByIdCommandBuilder.js' import type { UserV1GetUserByIdInputParameter, UserV1GetUserByIdInputPayload } from './types.js' describe('service User version 1 - command getUserById', () => { - let sandbox = createSandbox() - beforeEach(() => { - sandbox = createSandbox() - }) + let sandbox = createSandbox() + beforeEach(() => { + sandbox = createSandbox() + }) - afterEach(() => { - sandbox.restore() - }) + afterEach(() => { + sandbox.restore() + }) - test('returns a user', async () => { - const service = await userV1Service.getInstance(getEventBridgeMock(sandbox).mock, { - logger: getLoggerMock(sandbox).mock, - }) + test('returns a user', async () => { + const service = await userV1Service.getInstance(getEventBridgeMock(sandbox).mock, { + logger: getLoggerMock(sandbox).mock, + }) - const getUserById = safeBind(getUserByIdCommandBuilder.getCommandFunction(), service) + const getUserById = safeBind(getUserByIdCommandBuilder.getCommandFunction(), service) - const payload: UserV1GetUserByIdInputPayload = undefined + const payload: UserV1GetUserByIdInputPayload = undefined - const userMock: User = { - email: 'email@example.com', - name: 'test user', - password: 'password', - userId: 'a5fef052-911c-472c-ac25-e2da327f0af5', - } + const userMock: User = { + email: 'email@example.com', + name: 'test user', + password: 'password', + userId: 'a5fef052-911c-472c-ac25-e2da327f0af5', + } - const parameter: UserV1GetUserByIdInputParameter = { - userId: userMock.userId, - } + const parameter: UserV1GetUserByIdInputParameter = { + userId: userMock.userId, + } - const context = getUserByIdCommandBuilder.getCommandContextMock(payload, parameter, sandbox) + const context = getUserByIdCommandBuilder.getCommandContextMock({ payload, parameter, sandbox }) - context.stubs.getState.resolves({ [StateStoreKey.Users]: [userMock] }) + context.stubs.getState.resolves({ [StateStoreKey.Users]: [userMock] }) - const result = await getUserById(context.mock, payload, parameter) + const result = await getUserById(context.mock, payload, parameter) - expect(result).toStrictEqual({ email: userMock.email, name: userMock.name, userId: userMock.userId }) - }) + expect(result).toStrictEqual({ email: userMock.email, name: userMock.name, userId: userMock.userId }) + }) - test('throws if user can not be found', async () => { - const service = await userV1Service.getInstance(getEventBridgeMock(sandbox).mock, { - logger: getLoggerMock(sandbox).mock, - }) + test('throws if user can not be found', async () => { + const service = await userV1Service.getInstance(getEventBridgeMock(sandbox).mock, { + logger: getLoggerMock(sandbox).mock, + }) - const getUserById = safeBind(getUserByIdCommandBuilder.getCommandFunction(), service) + const getUserById = safeBind(getUserByIdCommandBuilder.getCommandFunction(), service) - const payload: UserV1GetUserByIdInputPayload = undefined + const payload: UserV1GetUserByIdInputPayload = undefined - const userMock: User = { - email: 'email@example.com', - name: 'test user', - password: 'password', - userId: 'a5fef052-911c-472c-ac25-e2da327f0af5', - } + const userMock: User = { + email: 'email@example.com', + name: 'test user', + password: 'password', + userId: 'a5fef052-911c-472c-ac25-e2da327f0af5', + } - const parameter: UserV1GetUserByIdInputParameter = { - userId: userMock.userId, - } + const parameter: UserV1GetUserByIdInputParameter = { + userId: userMock.userId, + } - const context = getUserByIdCommandBuilder.getCommandContextMock(payload, parameter, sandbox) + const context = getUserByIdCommandBuilder.getCommandContextMock({ payload, parameter, sandbox }) - context.stubs.getState.resolves({ [StateStoreKey.Users]: [] }) + context.stubs.getState.resolves({ [StateStoreKey.Users]: [] }) - await expect(getUserById(context.mock, payload, parameter)).rejects.toThrow('user could not be found') - }) + await expect(getUserById(context.mock, payload, parameter)).rejects.toThrow('user could not be found') + }) }) diff --git a/examples/fullexample/src/service/user/v1/command/getUserById/getUserByIdCommandBuilder.ts b/examples/fullexample/src/service/user/v1/command/getUserById/getUserByIdCommandBuilder.ts index 8d8e25d5f..5256e3242 100644 --- a/examples/fullexample/src/service/user/v1/command/getUserById/getUserByIdCommandBuilder.ts +++ b/examples/fullexample/src/service/user/v1/command/getUserById/getUserByIdCommandBuilder.ts @@ -4,26 +4,26 @@ import type { User } from '../../../../../types/index.js' import { StateStoreKey } from '../../../../../types/index.js' import { userV1ServiceBuilder } from '../../userV1ServiceBuilder.js' import { - userV1GetUserByIdInputParameterSchema, - userV1GetUserByIdInputPayloadSchema, - userV1GetUserByIdOutputPayloadSchema, + userV1GetUserByIdInputParameterSchema, + userV1GetUserByIdInputPayloadSchema, + userV1GetUserByIdOutputPayloadSchema, } from './schema.js' export const getUserByIdCommandBuilder = userV1ServiceBuilder - .getCommandBuilder('getUserById', 'returns the user given by the user id') - .addPayloadSchema(userV1GetUserByIdInputPayloadSchema) - .addParameterSchema(userV1GetUserByIdInputParameterSchema) - .addOutputSchema(userV1GetUserByIdOutputPayloadSchema) - .exposeAsHttpEndpoint('GET', 'user/:userId') - .setCommandFunction(async function (context, _payload, parameter) { - const result = (await context.states.getState(StateStoreKey.Users)) as { [StateStoreKey.Users]: User[] | undefined } - const users = result.users ?? [] + .getCommandBuilder('getUserById', 'returns the user given by the user id') + .addPayloadSchema(userV1GetUserByIdInputPayloadSchema) + .addParameterSchema(userV1GetUserByIdInputParameterSchema) + .addOutputSchema(userV1GetUserByIdOutputPayloadSchema) + .exposeAsHttpEndpoint('GET', 'user/:userId') + .setCommandFunction(async function (context, _payload, parameter) { + const result = (await context.states.getState(StateStoreKey.Users)) as { [StateStoreKey.Users]: User[] | undefined } + const users = result.users ?? [] - const user = users.find((user) => (user.userId = parameter.userId)) + const user = users.find(user => user.userId === parameter.userId) - if (!user) { - throw new HandledError(StatusCode.NotFound, 'user could not be found', { userId: parameter.userId }) - } + if (!user) { + throw new HandledError(StatusCode.NotFound, 'user could not be found', { userId: parameter.userId }) + } - return user - }) + return user + }) diff --git a/examples/fullexample/src/service/user/v1/command/getUserById/schema.ts b/examples/fullexample/src/service/user/v1/command/getUserById/schema.ts index 87b2549bb..06a7616ec 100644 --- a/examples/fullexample/src/service/user/v1/command/getUserById/schema.ts +++ b/examples/fullexample/src/service/user/v1/command/getUserById/schema.ts @@ -3,10 +3,10 @@ import { z } from 'zod' // define the input parameters export const userV1GetUserByIdInputParameterSchema = extendApi( - z.object({ - userId: extendApi(z.string().uuid(), { title: 'the user id', example: 'a5fef052-911c-472c-ac25-e2da327f0af5' }), - }), - { title: 'get user by id input payload schema' }, + z.object({ + userId: extendApi(z.string().uuid(), { title: 'the user id', example: 'a5fef052-911c-472c-ac25-e2da327f0af5' }), + }), + { title: 'get user by id input payload schema' }, ) // define the input payload @@ -14,13 +14,13 @@ export const userV1GetUserByIdInputPayloadSchema = z.undefined() // define the output payload export const userV1GetUserByIdOutputPayloadSchema = extendApi( - z.object({ - userId: extendApi(z.string().uuid(), { title: 'the user id', example: 'a5fef052-911c-472c-ac25-e2da327f0af5' }), - email: extendApi(z.string().email(), { title: 'the email of the user to register', example: 'user@email.com' }), - name: extendApi(z.string().min(3), { - title: 'the name of the user to register', - example: 'User', - }), - }), - { title: 'the sign up input' }, + z.object({ + userId: extendApi(z.string().uuid(), { title: 'the user id', example: 'a5fef052-911c-472c-ac25-e2da327f0af5' }), + email: extendApi(z.string().email(), { title: 'the email of the user to register', example: 'user@email.com' }), + name: extendApi(z.string().min(3), { + title: 'the name of the user to register', + example: 'User', + }), + }), + { title: 'the sign up input' }, ) diff --git a/examples/fullexample/src/service/user/v1/command/getUserById/types.ts b/examples/fullexample/src/service/user/v1/command/getUserById/types.ts index 7a3c6290c..59d70c337 100644 --- a/examples/fullexample/src/service/user/v1/command/getUserById/types.ts +++ b/examples/fullexample/src/service/user/v1/command/getUserById/types.ts @@ -1,9 +1,9 @@ import type { z } from 'zod' import type { - userV1GetUserByIdInputParameterSchema, - userV1GetUserByIdInputPayloadSchema, - userV1GetUserByIdOutputPayloadSchema, + userV1GetUserByIdInputParameterSchema, + userV1GetUserByIdInputPayloadSchema, + userV1GetUserByIdOutputPayloadSchema, } from './schema.js' export type UserV1GetUserByIdInputParameter = z.input diff --git a/examples/fullexample/src/service/user/v1/command/signUp/schema.ts b/examples/fullexample/src/service/user/v1/command/signUp/schema.ts index f969055f8..17d29b986 100644 --- a/examples/fullexample/src/service/user/v1/command/signUp/schema.ts +++ b/examples/fullexample/src/service/user/v1/command/signUp/schema.ts @@ -6,24 +6,24 @@ export const userV1SignUpInputParameterSchema = extendApi(z.object({}), { title: // define the input payload export const userV1SignUpInputPayloadSchema = extendApi( - z.object({ - email: extendApi(z.string().email(), { title: 'the email of the user to register', example: 'user@email.com' }), - name: extendApi(z.string().min(3), { - title: 'the name of the user to register', - example: 'User', - }), - password: extendApi(z.string().min(3), { - title: 'the name login password', - example: 'password', - }), - }), - { title: 'the sign up input' }, + z.object({ + email: extendApi(z.string().email(), { title: 'the email of the user to register', example: 'user@email.com' }), + name: extendApi(z.string().min(3), { + title: 'the name of the user to register', + example: 'User', + }), + password: extendApi(z.string().min(3), { + title: 'the name login password', + example: 'password', + }), + }), + { title: 'the sign up input' }, ) // define the output payload export const userV1SignUpOutputPayloadSchema = extendApi( - z.object({ - userId: extendApi(z.string().uuid(), { title: 'the user id', example: 'a5fef052-911c-472c-ac25-e2da327f0af5' }), - }), - { title: 'sign up output payload schema' }, + z.object({ + userId: extendApi(z.string().uuid(), { title: 'the user id', example: 'a5fef052-911c-472c-ac25-e2da327f0af5' }), + }), + { title: 'sign up output payload schema' }, ) diff --git a/examples/fullexample/src/service/user/v1/command/signUp/signUp.test.ts b/examples/fullexample/src/service/user/v1/command/signUp/signUp.test.ts index 5b4f31157..31bac83af 100644 --- a/examples/fullexample/src/service/user/v1/command/signUp/signUp.test.ts +++ b/examples/fullexample/src/service/user/v1/command/signUp/signUp.test.ts @@ -7,69 +7,69 @@ import { signUpCommandBuilder } from './signUpCommandBuilder.js' import type { UserV1SignUpInputParameter, UserV1SignUpInputPayload } from './types.js' describe('service User version 1 - command signUp', () => { - let sandbox = createSandbox() - beforeEach(() => { - sandbox = createSandbox() - }) + let sandbox = createSandbox() + beforeEach(() => { + sandbox = createSandbox() + }) - afterEach(() => { - sandbox.restore() - }) + afterEach(() => { + sandbox.restore() + }) - test('can register a new user', async () => { - const service = await userV1Service.getInstance(getEventBridgeMock(sandbox).mock, { - logger: getLoggerMock(sandbox).mock, - }) + test('can register a new user', async () => { + const service = await userV1Service.getInstance(getEventBridgeMock(sandbox).mock, { + logger: getLoggerMock(sandbox).mock, + }) - const signUp = safeBind(signUpCommandBuilder.getCommandFunction(), service) + const signUp = safeBind(signUpCommandBuilder.getCommandFunction(), service) - const payload: UserV1SignUpInputPayload = { - name: 'test user', - email: 'email@example.com', - password: 'password', - } + const payload: UserV1SignUpInputPayload = { + name: 'test user', + email: 'email@example.com', + password: 'password', + } - const parameter: UserV1SignUpInputParameter = {} + const parameter: UserV1SignUpInputParameter = {} - const context = signUpCommandBuilder.getCommandContextMock(payload, parameter, sandbox) + const context = signUpCommandBuilder.getCommandContextMock({ payload, parameter, sandbox }) - context.stubs.getState.resolves({}) - context.stubs.setState.resolves() + context.stubs.getState.resolves({}) + context.stubs.setState.resolves() - const result = await signUp(context.mock, payload, parameter) + const result = await signUp(context.mock, payload, parameter) - expect(result.userId).toBeDefined() - }) + expect(result.userId).toBeDefined() + }) - test('throws when a user with same email exist', async () => { - const service = await userV1Service.getInstance(getEventBridgeMock(sandbox).mock, { - logger: getLoggerMock(sandbox).mock, - }) + test('throws when a user with same email exist', async () => { + const service = await userV1Service.getInstance(getEventBridgeMock(sandbox).mock, { + logger: getLoggerMock(sandbox).mock, + }) - const signUp = safeBind(signUpCommandBuilder.getCommandFunction(), service) + const signUp = safeBind(signUpCommandBuilder.getCommandFunction(), service) - const payload: UserV1SignUpInputPayload = { - name: 'test user', - email: 'email@example.com', - password: 'password', - } + const payload: UserV1SignUpInputPayload = { + name: 'test user', + email: 'email@example.com', + password: 'password', + } - const parameter: UserV1SignUpInputParameter = {} + const parameter: UserV1SignUpInputParameter = {} - const context = signUpCommandBuilder.getCommandContextMock(payload, parameter, sandbox) + const context = signUpCommandBuilder.getCommandContextMock({ payload, parameter, sandbox }) - context.stubs.getState.resolves({ - [StateStoreKey.Users]: [ - { - name: 'test user', - email: 'email@example.com', - password: 'password', - userId: 'a5fef052-911c-472c-ac25-e2da327f0af5', - }, - ], - }) - context.stubs.setState.resolves() + context.stubs.getState.resolves({ + [StateStoreKey.Users]: [ + { + name: 'test user', + email: 'email@example.com', + password: 'password', + userId: 'a5fef052-911c-472c-ac25-e2da327f0af5', + }, + ], + }) + context.stubs.setState.resolves() - await expect(signUp(context.mock, payload, parameter)).rejects.toThrow('the user already exists') - }) + await expect(signUp(context.mock, payload, parameter)).rejects.toThrow('the user already exists') + }) }) diff --git a/examples/fullexample/src/service/user/v1/command/signUp/signUpCommandBuilder.ts b/examples/fullexample/src/service/user/v1/command/signUp/signUpCommandBuilder.ts index b44bbb80c..729c69670 100644 --- a/examples/fullexample/src/service/user/v1/command/signUp/signUpCommandBuilder.ts +++ b/examples/fullexample/src/service/user/v1/command/signUp/signUpCommandBuilder.ts @@ -7,37 +7,37 @@ import { StateStoreKey } from '../../../../../types/index.js' import { ServiceEvent } from '../../../../ServiceEvent.enum.js' import { userV1ServiceBuilder } from '../../userV1ServiceBuilder.js' import { - userV1SignUpInputParameterSchema, - userV1SignUpInputPayloadSchema, - userV1SignUpOutputPayloadSchema, + userV1SignUpInputParameterSchema, + userV1SignUpInputPayloadSchema, + userV1SignUpOutputPayloadSchema, } from './schema.js' export const signUpCommandBuilder = userV1ServiceBuilder - .getCommandBuilder('signUp', 'registers a new user at our product') - .setSuccessEventName(ServiceEvent.NewUserRegistered) - .addPayloadSchema(userV1SignUpInputPayloadSchema) - .addParameterSchema(userV1SignUpInputParameterSchema) - .addOutputSchema(userV1SignUpOutputPayloadSchema) - .exposeAsHttpEndpoint('POST', 'user/signup') - .setCommandFunction(async function (context, payload, _parameter) { - const result = (await context.states.getState(StateStoreKey.Users)) as { [StateStoreKey.Users]: User[] | undefined } - - if (result.users?.some((user) => user.email === payload.email)) { - throw new HandledError(StatusCode.BadRequest, 'the user already exists') - } - - const user: User = { - ...payload, - userId: randomUUID(), - } - - const users = result.users ?? [] - users.push(user) - - await context.states.setState(StateStoreKey.Users, users) - - // add your business logic here - context.logger.info('new user added') - - return user - }) + .getCommandBuilder('signUp', 'registers a new user at our product') + .setSuccessEventName(ServiceEvent.NewUserRegistered) + .addPayloadSchema(userV1SignUpInputPayloadSchema) + .addParameterSchema(userV1SignUpInputParameterSchema) + .addOutputSchema(userV1SignUpOutputPayloadSchema) + .exposeAsHttpEndpoint('POST', 'user/signup') + .setCommandFunction(async function (context, payload, _parameter) { + const result = (await context.states.getState(StateStoreKey.Users)) as { [StateStoreKey.Users]: User[] | undefined } + + if (result.users?.some(user => user.email === payload.email)) { + throw new HandledError(StatusCode.BadRequest, 'the user already exists') + } + + const user: User = { + ...payload, + userId: randomUUID(), + } + + const users = result.users ?? [] + users.push(user) + + await context.states.setState(StateStoreKey.Users, users) + + // add your business logic here + context.logger.info('new user added') + + return user + }) diff --git a/examples/fullexample/src/service/user/v1/command/signUp/types.ts b/examples/fullexample/src/service/user/v1/command/signUp/types.ts index 084667e07..a162fc0d0 100644 --- a/examples/fullexample/src/service/user/v1/command/signUp/types.ts +++ b/examples/fullexample/src/service/user/v1/command/signUp/types.ts @@ -1,9 +1,9 @@ import type { z } from 'zod' import type { - userV1SignUpInputParameterSchema, - userV1SignUpInputPayloadSchema, - userV1SignUpOutputPayloadSchema, + userV1SignUpInputParameterSchema, + userV1SignUpInputPayloadSchema, + userV1SignUpOutputPayloadSchema, } from './schema.js' export type UserV1SignUpInputParameter = z.input diff --git a/examples/fullexample/src/service/user/v1/userV1Service.test.ts b/examples/fullexample/src/service/user/v1/userV1Service.test.ts index be59733e5..c49d08669 100644 --- a/examples/fullexample/src/service/user/v1/userV1Service.test.ts +++ b/examples/fullexample/src/service/user/v1/userV1Service.test.ts @@ -1,7 +1,7 @@ import { userV1Service as service } from './userV1Service.js' describe('service user version 1', () => { - it('has a valid setup', async () => { - await expect(service.testServiceSetup()).resolves.toBeTruthy() - }) + it('has a valid setup', async () => { + await expect(service.testServiceSetup()).resolves.toBeTruthy() + }) }) diff --git a/examples/fullexample/src/service/user/v1/userV1Service.ts b/examples/fullexample/src/service/user/v1/userV1Service.ts index b483dcc51..2d972c27b 100644 --- a/examples/fullexample/src/service/user/v1/userV1Service.ts +++ b/examples/fullexample/src/service/user/v1/userV1Service.ts @@ -10,13 +10,13 @@ import { userV1ServiceBuilder } from './userV1ServiceBuilder.js' // other service config should be done in ./userServiceBuilder.ts file const commandDefinitions: CommandDefinitionList = [ - signUpCommandBuilder.getDefinition(), - getUserByIdCommandBuilder.getDefinition(), - getAllUsersCommandBuilder.getDefinition(), + signUpCommandBuilder.getDefinition(), + getUserByIdCommandBuilder.getDefinition(), + getAllUsersCommandBuilder.getDefinition(), ] const subscriptionDefinitions: SubscriptionDefinitionList = [] export const userV1Service = userV1ServiceBuilder - .addCommandDefinition(...commandDefinitions) - .addSubscriptionDefinition(...subscriptionDefinitions) + .addCommandDefinition(...commandDefinitions) + .addSubscriptionDefinition(...subscriptionDefinitions) diff --git a/examples/fullexample/src/service/user/v1/userV1ServiceBuilder.ts b/examples/fullexample/src/service/user/v1/userV1ServiceBuilder.ts index b047cf552..57a76d32d 100644 --- a/examples/fullexample/src/service/user/v1/userV1ServiceBuilder.ts +++ b/examples/fullexample/src/service/user/v1/userV1ServiceBuilder.ts @@ -5,12 +5,10 @@ import { generalUserServiceInfo } from '../generalUserServiceInfo.js' import { userServiceV1ConfigSchema } from './userServiceConfig.js' export const userServiceInfo = { - serviceVersion: '1', - ...generalUserServiceInfo, + serviceVersion: '1', + ...generalUserServiceInfo, } as const satisfies ServiceInfoType // create a service builder instance and assign service config schema and default config. -export const userV1ServiceBuilder = new ServiceBuilder(userServiceInfo) - .setConfigSchema(userServiceV1ConfigSchema) - .setDefaultConfig({}) +export const userV1ServiceBuilder = new ServiceBuilder(userServiceInfo).setConfigSchema(userServiceV1ConfigSchema) diff --git a/examples/fullexample/src/types/StateStoreKey.enum.ts b/examples/fullexample/src/types/StateStoreKey.enum.ts index a25d9271c..7992eeae1 100644 --- a/examples/fullexample/src/types/StateStoreKey.enum.ts +++ b/examples/fullexample/src/types/StateStoreKey.enum.ts @@ -1,3 +1,3 @@ export enum StateStoreKey { - Users = 'users', + Users = 'users', } diff --git a/examples/fullexample/src/types/User.ts b/examples/fullexample/src/types/User.ts index 8e2616607..de6239fbf 100644 --- a/examples/fullexample/src/types/User.ts +++ b/examples/fullexample/src/types/User.ts @@ -1,6 +1,6 @@ export type User = { - email: string - name: string - password: string - userId: string + email: string + name: string + password: string + userId: string } diff --git a/examples/fullexample/tsconfig.json b/examples/fullexample/tsconfig.json index a84ca294e..53fcc919f 100644 --- a/examples/fullexample/tsconfig.json +++ b/examples/fullexample/tsconfig.json @@ -1,11 +1,9 @@ { - "extends": "../../tsconfig.json", - - "compilerOptions": { - "outDir": "./build" - }, - "files": [ - "src/index.ts", - "fastify.d.ts" - ], -} \ No newline at end of file + "extends": "../../tsconfig.json", + + "compilerOptions": { + "outDir": "./build", + "types": ["vitest/globals", "node"] + }, + "files": ["src/index.ts", "fastify.d.ts"] +} diff --git a/examples/hono-example/package.json b/examples/hono-example/package.json index 3b9b035ce..becf9d3ec 100644 --- a/examples/hono-example/package.json +++ b/examples/hono-example/package.json @@ -1,39 +1,39 @@ { - "name": "@purista/hono-example", - "version": "1.11.0", - "description": "purista backend framework", - "homepage": "https://purista.dev", - "private": true, - "repository": { - "type": "git", - "url": "git@github.com:sebastianwessel/purista.git" - }, - "author": "Sebastian Wessel", - "license": "ISC", - "main": "src/index.ts", - "type": "module", - "engines": { - "node": ">=18.15" - }, - "scripts": { - "start": "tsx src/index.ts | pino-pretty", - "dev": "tsx watch src/index.ts | pino-pretty", - "lint": "eslint . --ext .ts,.json --cache . --fix", - "test": "vitest" - }, - "devDependencies": { - "@types/node": "^20.11.17", - "pino-pretty": "^10.3.1", - "sinon": "^17.0.1", - "tsx": "^4.7.0", - "typescript": "^5.3.3", - "vitest": "^1.3.0" - }, - "dependencies": { - "@hono/node-server": "^1.8.0", - "@hono/swagger-ui": "^0.2.1", - "@purista/core": "*", - "@purista/hono-http-server": "^1.9.0", - "zod": "3.22.4" - } + "name": "@purista/hono-example", + "version": "1.11.0", + "description": "purista backend framework", + "homepage": "https://purista.dev", + "private": true, + "repository": { + "type": "git", + "url": "git@github.com:puristajs/purista.git" + }, + "author": "Sebastian Wessel", + "license": "ISC", + "main": "src/index.ts", + "type": "module", + "engines": { + "node": ">=18.15" + }, + "scripts": { + "start": "tsx src/index.ts | pino-pretty", + "dev": "tsx watch src/index.ts | pino-pretty", + "lint": "npx @biomejs/biome check --write", + "test": "vitest" + }, + "devDependencies": { + "@types/node": "^22.5.1", + "pino-pretty": "^13.0.0", + "sinon": "^19.0.2", + "tsx": "^4.19.0", + "typescript": "^5.5.4", + "vitest": "^3.0.4" + }, + "dependencies": { + "@hono/node-server": "^1.12.2", + "@scalar/hono-api-reference": "^0.5.119", + "@purista/core": "*", + "@purista/hono-http-server": "^1.9.0", + "zod": "^3.24.1" + } } diff --git a/examples/hono-example/public/index.html b/examples/hono-example/public/index.html index 51805b539..e0be04244 100644 --- a/examples/hono-example/public/index.html +++ b/examples/hono-example/public/index.html @@ -25,7 +25,7 @@

PURISTA

You can use the PURISTA cli to quickly add services, commands and subscription
You can run the cli with purista add
- Optionally, you can directly tell, what kind of ressource you like to add:
+ Optionally, you can directly tell, what kind of resource you like to add:
Add a new service purista add service
Add a command to a existing service purista add command
Add a subscription to a existing service purista add subscription
diff --git a/examples/hono-example/src/index.ts b/examples/hono-example/src/index.ts index 66f97e8e4..e11392fa4 100644 --- a/examples/hono-example/src/index.ts +++ b/examples/hono-example/src/index.ts @@ -1,6 +1,7 @@ import { serve } from '@hono/node-server' import { serveStatic } from '@hono/node-server/serve-static' -import { swaggerUI } from '@hono/swagger-ui' +import { apiReference } from '@scalar/hono-api-reference' + import { DefaultEventBridge, initLogger } from '@purista/core' import { honoV1Service } from '@purista/hono-http-server' import { basicAuth } from 'hono/basic-auth' @@ -10,53 +11,61 @@ import { delayV1ServiceBuilder } from './service/delay/v1/index.js' import { pingV1Service } from './service/ping/v1/index.js' export const main = async () => { - const logger = initLogger('debug') - - // initiate the event bridge as first step - const eventBridge = new DefaultEventBridge() - await eventBridge.start() - - // add your service - const pingService = await pingV1Service.getInstance(eventBridge) - await pingService.start() - - // initiate the webserver service as second step - const honoService = await honoV1Service.getInstance(eventBridge, { - logger, - serviceConfig: { services: [pingService], enableDynamicRoutes: true }, - }) - - honoService.app.use('*', compress()) - honoService.app.get('/api', swaggerUI({ url: '/api/openapi.json' })) - honoService.app.get('*', serveStatic({ root: './public' })) - honoService.openApi.addSecurityScheme('basicAuth', { type: 'http', scheme: 'basic' }) - honoService.openApi.addServer({ url: 'http://localhost:3000', description: 'the local server' }) - - honoService - .setHonoTypes<{ Variables: { customVar: string } }>() - .setHealthFunction(async function () { - this.logger.info('custom health check') - }) - .setProtectMiddleware(async function (c, next) { - const auth = basicAuth({ username: 'user', password: 'password' }) - c.set('additionalParameter', { userId: '123' }) - c.set('customVar', 'some custom var value') - return auth(c, next) - }) - - // start the webserver - await honoService.start() - - const _serverInstance = serve({ - fetch: honoService.app.fetch, - port: 3000, - }) - - logger.info('Wait 10 secs until a new service starts') - await new Promise((resolve) => setTimeout(() => resolve(true), 10_000)) - - const delayedService = await delayV1ServiceBuilder.getInstance(eventBridge, { logger }) - await delayedService.start() + const logger = initLogger('debug') + + // initiate the event bridge as first step + const eventBridge = new DefaultEventBridge() + await eventBridge.start() + + // add your service + const pingService = await pingV1Service.getInstance(eventBridge, {}) + await pingService.start() + + // initiate the webserver service as second step + const honoService = await honoV1Service.getInstance(eventBridge, { + logger, + serviceConfig: { services: [pingService], enableDynamicRoutes: true }, + }) + + honoService.app.use('*', compress()) + honoService.app.get( + '/api', + apiReference({ + spec: { + url: '/api/openapi.json', + }, + }), + ) + + honoService.app.get('*', serveStatic({ root: './public' })) + honoService.openApi.addSecurityScheme('basicAuth', { type: 'http', scheme: 'basic' }) + honoService.openApi.addServer({ url: 'http://localhost:3000', description: 'the local server' }) + + honoService + .setHonoTypes<{ Variables: { customVar: string } }>() + .setHealthFunction(async function () { + this.logger.info('custom health check') + }) + .setProtectMiddleware(async function (c, next) { + const auth = basicAuth({ username: 'user', password: 'password' }) + c.set('additionalParameter', { userId: '123' }) + c.set('customVar', 'some custom var value') + return auth(c, next) + }) + + // start the webserver + await honoService.start() + + const _serverInstance = serve({ + fetch: honoService.app.fetch, + port: 3000, + }) + + logger.info('Wait 10 secs until a new service starts') + await new Promise(resolve => setTimeout(() => resolve(true), 10_000)) + + const delayedService = await delayV1ServiceBuilder.getInstance(eventBridge, { logger }) + await delayedService.start() } main() diff --git a/examples/hono-example/src/service/ServiceEvent.enum.ts b/examples/hono-example/src/service/ServiceEvent.enum.ts index 875bffb27..415d9a061 100644 --- a/examples/hono-example/src/service/ServiceEvent.enum.ts +++ b/examples/hono-example/src/service/ServiceEvent.enum.ts @@ -1,7 +1,7 @@ export enum ServiceEvent { - /** - * Emitted by ping v1 command ping: - * the ping command exposed as http endpoint - */ - Pinged = 'pinged', + /** + * Emitted by ping v1 command ping: + * the ping command exposed as http endpoint + */ + Pinged = 'pinged', } diff --git a/examples/hono-example/src/service/delay/generalDelayServiceInfo.ts b/examples/hono-example/src/service/delay/generalDelayServiceInfo.ts index 38aacf319..ba8602d55 100644 --- a/examples/hono-example/src/service/delay/generalDelayServiceInfo.ts +++ b/examples/hono-example/src/service/delay/generalDelayServiceInfo.ts @@ -1,6 +1,6 @@ import type { ServiceInfoType } from '@purista/core' export const generalDelayServiceInfo = { - serviceName: 'Delay', - serviceDescription: 'Example to show a service which starts after webserver and registers commands', + serviceName: 'Delay', + serviceDescription: 'Example to show a service which starts after webserver and registers commands', } as const satisfies Omit diff --git a/examples/hono-example/src/service/delay/v1/command/fooBar/fooBar.test.ts b/examples/hono-example/src/service/delay/v1/command/fooBar/fooBar.test.ts index bffbb4617..5365c51aa 100644 --- a/examples/hono-example/src/service/delay/v1/command/fooBar/fooBar.test.ts +++ b/examples/hono-example/src/service/delay/v1/command/fooBar/fooBar.test.ts @@ -1,4 +1,4 @@ -import { getCommandContextMock, getEventBridgeMock, getLoggerMock, safeBind } from '@purista/core' +import { getEventBridgeMock, getLoggerMock, safeBind } from '@purista/core' import { createSandbox } from 'sinon' import { delayV1Service } from '../../delayV1Service.js' @@ -6,35 +6,35 @@ import { fooBarCommandBuilder } from './fooBarCommandBuilder.js' import type { DelayV1FooBarInputParameter, DelayV1FooBarInputPayload } from './types.js' describe('service Delay version 1 - command fooBar', () => { - let sandbox = createSandbox() - beforeEach(() => { - sandbox = createSandbox() - }) + let sandbox = createSandbox() + beforeEach(() => { + sandbox = createSandbox() + }) - afterEach(() => { - sandbox.restore() - }) + afterEach(() => { + sandbox.restore() + }) - test('does not throw', async () => { - const service = await delayV1Service.getInstance(getEventBridgeMock(sandbox).mock, { - logger: getLoggerMock(sandbox).mock, - }) + test('does not throw', async () => { + const service = await delayV1Service.getInstance(getEventBridgeMock(sandbox).mock, { + logger: getLoggerMock(sandbox).mock, + }) - const fooBar = safeBind(fooBarCommandBuilder.getCommandFunction(), service) + const fooBar = safeBind(fooBarCommandBuilder.getCommandFunction(), service) - const payload: DelayV1FooBarInputPayload = undefined + const payload: DelayV1FooBarInputPayload = undefined - const parameter: DelayV1FooBarInputParameter = { - p: 'the_p', - } + const parameter: DelayV1FooBarInputParameter = { + p: 'the_p', + } - const context = getCommandContextMock(payload, parameter, sandbox) + const context = fooBarCommandBuilder.getCommandContextMock({ payload, parameter, sandbox }) - const result = await fooBar(context.mock, payload, parameter) + const result = await fooBar(context.mock, payload, parameter) - expect(result).toStrictEqual({ - foo: 'bar', - parameter: { p: 'the_p' }, - }) - }) + expect(result).toStrictEqual({ + foo: 'bar', + parameter: { p: 'the_p' }, + }) + }) }) diff --git a/examples/hono-example/src/service/delay/v1/command/fooBar/fooBarCommandBuilder.ts b/examples/hono-example/src/service/delay/v1/command/fooBar/fooBarCommandBuilder.ts index 2b1d050b2..1da1f1350 100644 --- a/examples/hono-example/src/service/delay/v1/command/fooBar/fooBarCommandBuilder.ts +++ b/examples/hono-example/src/service/delay/v1/command/fooBar/fooBarCommandBuilder.ts @@ -1,17 +1,17 @@ import { delayV1ServiceBuilder } from '../../delayV1ServiceBuilder.js' import { - delayV1FooBarInputParameterSchema, - delayV1FooBarInputPayloadSchema, - delayV1FooBarOutputPayloadSchema, + delayV1FooBarInputParameterSchema, + delayV1FooBarInputPayloadSchema, + delayV1FooBarOutputPayloadSchema, } from './schema.js' export const fooBarCommandBuilder = delayV1ServiceBuilder - .getCommandBuilder('fooBar', 'Example for an exposed command') - .addPayloadSchema(delayV1FooBarInputPayloadSchema) - .addParameterSchema(delayV1FooBarInputParameterSchema) - .addOutputSchema(delayV1FooBarOutputPayloadSchema) - .disableHttpSecurity() - .exposeAsHttpEndpoint('GET', 'foo-bar/:p/:q?') - .setCommandFunction(async function (_context, _payload, parameter) { - return { foo: 'bar', parameter } - }) + .getCommandBuilder('fooBar', 'Example for an exposed command') + .addPayloadSchema(delayV1FooBarInputPayloadSchema) + .addParameterSchema(delayV1FooBarInputParameterSchema) + .addOutputSchema(delayV1FooBarOutputPayloadSchema) + .disableHttpSecurity() + .exposeAsHttpEndpoint('GET', 'foo-bar/:p/:q?') + .setCommandFunction(async function (_context, _payload, parameter) { + return { foo: 'bar', parameter } + }) diff --git a/examples/hono-example/src/service/delay/v1/command/fooBar/schema.ts b/examples/hono-example/src/service/delay/v1/command/fooBar/schema.ts index 4b4ab90d7..e2e15ef1c 100644 --- a/examples/hono-example/src/service/delay/v1/command/fooBar/schema.ts +++ b/examples/hono-example/src/service/delay/v1/command/fooBar/schema.ts @@ -3,11 +3,11 @@ import { z } from 'zod' // define the input parameters export const delayV1FooBarInputParameterSchema = extendApi( - z.object({ - p: z.string(), - q: z.string().optional(), - }), - { title: 'fooBar input parameter schema' }, + z.object({ + p: z.string(), + q: z.string().optional(), + }), + { title: 'fooBar input parameter schema' }, ) // define the input payload diff --git a/examples/hono-example/src/service/delay/v1/command/fooBar/types.ts b/examples/hono-example/src/service/delay/v1/command/fooBar/types.ts index 5283ed53d..5106c19a1 100644 --- a/examples/hono-example/src/service/delay/v1/command/fooBar/types.ts +++ b/examples/hono-example/src/service/delay/v1/command/fooBar/types.ts @@ -1,9 +1,9 @@ import type { z } from 'zod' import type { - delayV1FooBarInputParameterSchema, - delayV1FooBarInputPayloadSchema, - delayV1FooBarOutputPayloadSchema, + delayV1FooBarInputParameterSchema, + delayV1FooBarInputPayloadSchema, + delayV1FooBarOutputPayloadSchema, } from './schema.js' export type DelayV1FooBarInputParameter = z.input diff --git a/examples/hono-example/src/service/delay/v1/delayV1Service.test.ts b/examples/hono-example/src/service/delay/v1/delayV1Service.test.ts index 02df0bdce..0a54c4303 100644 --- a/examples/hono-example/src/service/delay/v1/delayV1Service.test.ts +++ b/examples/hono-example/src/service/delay/v1/delayV1Service.test.ts @@ -1,11 +1,7 @@ import { delayV1Service as service } from './delayV1Service.js' describe('service delay version 1', () => { - it('has valid commands', () => { - service.validateCommandDefinitions() - }) - - it('has valid subscriptions', () => { - service.validateSubscriptionDefinitions() - }) + it('has valid configuration', () => { + service.testServiceSetup() + }) }) diff --git a/examples/hono-example/src/service/delay/v1/delayV1Service.ts b/examples/hono-example/src/service/delay/v1/delayV1Service.ts index 195f576a3..3bb7e44a0 100644 --- a/examples/hono-example/src/service/delay/v1/delayV1Service.ts +++ b/examples/hono-example/src/service/delay/v1/delayV1Service.ts @@ -12,5 +12,5 @@ const commandDefinitions: CommandDefinitionList = [fooBarCommandBuilder.get const subscriptionDefinitions: SubscriptionDefinitionList = [] export const delayV1Service = delayV1ServiceBuilder - .addCommandDefinition(...commandDefinitions) - .addSubscriptionDefinition(...subscriptionDefinitions) + .addCommandDefinition(...commandDefinitions) + .addSubscriptionDefinition(...subscriptionDefinitions) diff --git a/examples/hono-example/src/service/delay/v1/delayV1ServiceBuilder.ts b/examples/hono-example/src/service/delay/v1/delayV1ServiceBuilder.ts index 2c62aab26..65ba20158 100644 --- a/examples/hono-example/src/service/delay/v1/delayV1ServiceBuilder.ts +++ b/examples/hono-example/src/service/delay/v1/delayV1ServiceBuilder.ts @@ -5,12 +5,10 @@ import { generalDelayServiceInfo } from '../generalDelayServiceInfo.js' import { delayServiceV1ConfigSchema } from './delayServiceConfig.js' export const delayServiceInfo: ServiceInfoType = { - serviceVersion: '1', - ...generalDelayServiceInfo, + serviceVersion: '1', + ...generalDelayServiceInfo, } // create a service builder instance and assign service config schema and default config. -export const delayV1ServiceBuilder = new ServiceBuilder(delayServiceInfo) - .setConfigSchema(delayServiceV1ConfigSchema) - .setDefaultConfig({}) +export const delayV1ServiceBuilder = new ServiceBuilder(delayServiceInfo).setConfigSchema(delayServiceV1ConfigSchema) diff --git a/examples/hono-example/src/service/ping/generalPingServiceInfo.ts b/examples/hono-example/src/service/ping/generalPingServiceInfo.ts index 4c1506fb3..21ae3da12 100644 --- a/examples/hono-example/src/service/ping/generalPingServiceInfo.ts +++ b/examples/hono-example/src/service/ping/generalPingServiceInfo.ts @@ -1,6 +1,6 @@ import type { ServiceInfoType } from '@purista/core' export const generalPingServiceInfo = { - serviceName: 'Ping', - serviceDescription: 'Example ping service', + serviceName: 'Ping', + serviceDescription: 'Example ping service', } as const satisfies Omit diff --git a/examples/hono-example/src/service/ping/v1/command/delete/deleteCommandBuilder.ts b/examples/hono-example/src/service/ping/v1/command/delete/deleteCommandBuilder.ts index 4808979a0..5d1e00238 100644 --- a/examples/hono-example/src/service/ping/v1/command/delete/deleteCommandBuilder.ts +++ b/examples/hono-example/src/service/ping/v1/command/delete/deleteCommandBuilder.ts @@ -1,14 +1,14 @@ import { pingV1ServiceBuilder } from '../../pingV1ServiceBuilder.js' import { - theServiceV1DeleteInputParameterSchema, - theServiceV1DeleteInputPayloadSchema, - theServiceV1DeleteOutputPayloadSchema, + theServiceV1DeleteInputParameterSchema, + theServiceV1DeleteInputPayloadSchema, + theServiceV1DeleteOutputPayloadSchema, } from './schema.js' export const deleteCommandBuilder = pingV1ServiceBuilder - .getCommandBuilder('delete', 'provide a dummy command') - .addPayloadSchema(theServiceV1DeleteInputPayloadSchema) - .addParameterSchema(theServiceV1DeleteInputParameterSchema) - .addOutputSchema(theServiceV1DeleteOutputPayloadSchema) - .exposeAsHttpEndpoint('DELETE', 'delete') - .setCommandFunction(async function (_context, _payload, _parameter) {}) + .getCommandBuilder('delete', 'provide a dummy command') + .addPayloadSchema(theServiceV1DeleteInputPayloadSchema) + .addParameterSchema(theServiceV1DeleteInputParameterSchema) + .addOutputSchema(theServiceV1DeleteOutputPayloadSchema) + .exposeAsHttpEndpoint('DELETE', 'delete') + .setCommandFunction(async function (_context, _payload, _parameter) {}) diff --git a/examples/hono-example/src/service/ping/v1/command/delete/schema.ts b/examples/hono-example/src/service/ping/v1/command/delete/schema.ts index 98a542b73..0fd7a0ae9 100644 --- a/examples/hono-example/src/service/ping/v1/command/delete/schema.ts +++ b/examples/hono-example/src/service/ping/v1/command/delete/schema.ts @@ -3,7 +3,7 @@ import { z } from 'zod' // define the input parameters export const theServiceV1DeleteInputParameterSchema = extendApi(z.object({}), { - title: 'delete input parameter schema', + title: 'delete input parameter schema', }) // define the input payload @@ -11,5 +11,5 @@ export const theServiceV1DeleteInputPayloadSchema = extendApi(z.any(), { title: // define the output payload export const theServiceV1DeleteOutputPayloadSchema = extendApi(z.void(), { - title: 'put output payload schema', + title: 'put output payload schema', }) diff --git a/examples/hono-example/src/service/ping/v1/command/delete/types.ts b/examples/hono-example/src/service/ping/v1/command/delete/types.ts index f6d611a99..61a4bfdc9 100644 --- a/examples/hono-example/src/service/ping/v1/command/delete/types.ts +++ b/examples/hono-example/src/service/ping/v1/command/delete/types.ts @@ -1,9 +1,9 @@ import type { z } from 'zod' import type { - theServiceV1DeleteInputParameterSchema, - theServiceV1DeleteInputPayloadSchema, - theServiceV1DeleteOutputPayloadSchema, + theServiceV1DeleteInputParameterSchema, + theServiceV1DeleteInputPayloadSchema, + theServiceV1DeleteOutputPayloadSchema, } from './schema.js' export type TheServiceV1DeleteInputParameter = z.input diff --git a/examples/hono-example/src/service/ping/v1/command/foo/foo.test.ts b/examples/hono-example/src/service/ping/v1/command/foo/foo.test.ts index a8bec6758..82b63ebdb 100644 --- a/examples/hono-example/src/service/ping/v1/command/foo/foo.test.ts +++ b/examples/hono-example/src/service/ping/v1/command/foo/foo.test.ts @@ -6,30 +6,30 @@ import { fooCommandBuilder } from './fooCommandBuilder.js' import type { PingV1FooInputParameter, PingV1FooInputPayload } from './types.js' describe('service Ping version 1 - command foo', () => { - let sandbox = createSandbox() - beforeEach(() => { - sandbox = createSandbox() - }) + let sandbox = createSandbox() + beforeEach(() => { + sandbox = createSandbox() + }) - afterEach(() => { - sandbox.restore() - }) + afterEach(() => { + sandbox.restore() + }) - test('does not throw', async () => { - const service = await pingV1Service.getInstance(getEventBridgeMock(sandbox).mock, { - logger: getLoggerMock(sandbox).mock, - }) + test('does not throw', async () => { + const service = await pingV1Service.getInstance(getEventBridgeMock(sandbox).mock, { + logger: getLoggerMock(sandbox).mock, + }) - const foo = safeBind(fooCommandBuilder.getCommandFunction(), service) + const foo = safeBind(fooCommandBuilder.getCommandFunction(), service) - const payload: PingV1FooInputPayload = undefined + const payload: PingV1FooInputPayload = undefined - const parameter: PingV1FooInputParameter = {} + const parameter: PingV1FooInputParameter = {} - const context = fooCommandBuilder.getCommandContextMock(payload, parameter, sandbox) + const context = fooCommandBuilder.getCommandContextMock({ payload, parameter, sandbox }) - const result = await foo(context.mock, payload, parameter) + const result = await foo(context.mock, payload, parameter) - expect(result).toStrictEqual({ foo: 'foo' }) - }) + expect(result).toStrictEqual({ foo: 'foo' }) + }) }) diff --git a/examples/hono-example/src/service/ping/v1/command/foo/fooCommandBuilder.ts b/examples/hono-example/src/service/ping/v1/command/foo/fooCommandBuilder.ts index 3a2988ed5..7df66ac12 100644 --- a/examples/hono-example/src/service/ping/v1/command/foo/fooCommandBuilder.ts +++ b/examples/hono-example/src/service/ping/v1/command/foo/fooCommandBuilder.ts @@ -2,15 +2,15 @@ import { pingV1ServiceBuilder } from '../../pingV1ServiceBuilder.js' import { pingV1FooInputParameterSchema, pingV1FooInputPayloadSchema, pingV1FooOutputPayloadSchema } from './schema.js' export const fooCommandBuilder = pingV1ServiceBuilder - .getCommandBuilder('foo', 'Calls foo command') - .addPayloadSchema(pingV1FooInputPayloadSchema) - .addParameterSchema(pingV1FooInputParameterSchema) - .addOutputSchema(pingV1FooOutputPayloadSchema) - .exposeAsHttpEndpoint('GET', 'foo') - .enableHttpSecurity() - .setCommandFunction(async function (_context, _payload, _parameter) { - // add your business logic here - return { - foo: 'foo', - } - }) + .getCommandBuilder('foo', 'Calls foo command') + .addPayloadSchema(pingV1FooInputPayloadSchema) + .addParameterSchema(pingV1FooInputParameterSchema) + .addOutputSchema(pingV1FooOutputPayloadSchema) + .exposeAsHttpEndpoint('GET', 'foo') + .enableHttpSecurity() + .setCommandFunction(async function (_context, _payload, _parameter) { + // add your business logic here + return { + foo: 'foo', + } + }) diff --git a/examples/hono-example/src/service/ping/v1/command/foo/schema.ts b/examples/hono-example/src/service/ping/v1/command/foo/schema.ts index afabaf493..15b065f27 100644 --- a/examples/hono-example/src/service/ping/v1/command/foo/schema.ts +++ b/examples/hono-example/src/service/ping/v1/command/foo/schema.ts @@ -9,8 +9,8 @@ export const pingV1FooInputPayloadSchema = z.undefined() // define the output payload export const pingV1FooOutputPayloadSchema = extendApi( - z.object({ - foo: z.string(), - }), - { title: 'foo output payload schema' }, + z.object({ + foo: z.string(), + }), + { title: 'foo output payload schema' }, ) diff --git a/examples/hono-example/src/service/ping/v1/command/foo/types.ts b/examples/hono-example/src/service/ping/v1/command/foo/types.ts index af6bd5339..d8a263a2a 100644 --- a/examples/hono-example/src/service/ping/v1/command/foo/types.ts +++ b/examples/hono-example/src/service/ping/v1/command/foo/types.ts @@ -1,9 +1,9 @@ import type { z } from 'zod' import type { - pingV1FooInputParameterSchema, - pingV1FooInputPayloadSchema, - pingV1FooOutputPayloadSchema, + pingV1FooInputParameterSchema, + pingV1FooInputPayloadSchema, + pingV1FooOutputPayloadSchema, } from './schema.js' export type PingV1FooInputParameter = z.input diff --git a/examples/hono-example/src/service/ping/v1/command/paramTest/paramTest.test.ts b/examples/hono-example/src/service/ping/v1/command/paramTest/paramTest.test.ts index 32b586969..9b01ff0bd 100644 --- a/examples/hono-example/src/service/ping/v1/command/paramTest/paramTest.test.ts +++ b/examples/hono-example/src/service/ping/v1/command/paramTest/paramTest.test.ts @@ -1,4 +1,4 @@ -import { getCommandContextMock, getEventBridgeMock, getLoggerMock, safeBind } from '@purista/core' +import { getEventBridgeMock, getLoggerMock, safeBind } from '@purista/core' import { createSandbox } from 'sinon' import { pingV1Service } from '../../pingV1Service.js' @@ -6,33 +6,33 @@ import { paramTestCommandBuilder } from './paramTestCommandBuilder.js' import type { PingV1ParamTestInputParameter, PingV1ParamTestInputPayload } from './types.js' describe('service Ping version 1 - command paramTest', () => { - let sandbox = createSandbox() - beforeEach(() => { - sandbox = createSandbox() - }) + let sandbox = createSandbox() + beforeEach(() => { + sandbox = createSandbox() + }) - afterEach(() => { - sandbox.restore() - }) + afterEach(() => { + sandbox.restore() + }) - test('does not throw', async () => { - const service = await pingV1Service.getInstance(getEventBridgeMock(sandbox).mock, { - logger: getLoggerMock(sandbox).mock, - }) + test('does not throw', async () => { + const service = await pingV1Service.getInstance(getEventBridgeMock(sandbox).mock, { + logger: getLoggerMock(sandbox).mock, + }) - const paramTest = safeBind(paramTestCommandBuilder.getCommandFunction(), service) + const paramTest = safeBind(paramTestCommandBuilder.getCommandFunction(), service) - const payload: PingV1ParamTestInputPayload = undefined + const payload: PingV1ParamTestInputPayload = undefined - const parameter: PingV1ParamTestInputParameter = { - requiredQuery: 'required', - requiredParam: 'required_id', - } + const parameter: PingV1ParamTestInputParameter = { + requiredQuery: 'required', + requiredParam: 'required_id', + } - const context = getCommandContextMock(payload, parameter, sandbox) + const context = paramTestCommandBuilder.getCommandContextMock({ payload, parameter, sandbox }) - const result = await paramTest(context.mock, payload, parameter) + const result = await paramTest(context.mock, payload, parameter) - expect(result).toStrictEqual({ parameter: { requiredParam: 'required_id', requiredQuery: 'required' } }) - }) + expect(result).toStrictEqual({ parameter: { requiredParam: 'required_id', requiredQuery: 'required' } }) + }) }) diff --git a/examples/hono-example/src/service/ping/v1/command/paramTest/paramTestCommandBuilder.ts b/examples/hono-example/src/service/ping/v1/command/paramTest/paramTestCommandBuilder.ts index 6e508b4c0..16aeecca0 100644 --- a/examples/hono-example/src/service/ping/v1/command/paramTest/paramTestCommandBuilder.ts +++ b/examples/hono-example/src/service/ping/v1/command/paramTest/paramTestCommandBuilder.ts @@ -1,22 +1,22 @@ import { pingV1ServiceBuilder } from '../../pingV1ServiceBuilder.js' import { - pingV1ParamTestInputParameterSchema, - pingV1ParamTestInputPayloadSchema, - pingV1ParamTestOutputPayloadSchema, + pingV1ParamTestInputParameterSchema, + pingV1ParamTestInputPayloadSchema, + pingV1ParamTestOutputPayloadSchema, } from './schema.js' export const paramTestCommandBuilder = pingV1ServiceBuilder - .getCommandBuilder('paramTest', 'Show how to use path parmater and query params') - .addPayloadSchema(pingV1ParamTestInputPayloadSchema) - .addParameterSchema(pingV1ParamTestInputParameterSchema) - .addOutputSchema(pingV1ParamTestOutputPayloadSchema) - .exposeAsHttpEndpoint('GET', 'param/:requiredParam/:optionalParam?') - .disableHttpSecurity() - .addQueryParameters({ name: 'optionalQuery', required: false }, { name: 'requiredQuery', required: true }) - .setCommandFunction(async function (_context, _payload, parameter) { - // add your business logic here + .getCommandBuilder('paramTest', 'Show how to use path parmater and query params') + .addPayloadSchema(pingV1ParamTestInputPayloadSchema) + .addParameterSchema(pingV1ParamTestInputParameterSchema) + .addOutputSchema(pingV1ParamTestOutputPayloadSchema) + .exposeAsHttpEndpoint('GET', 'param/:requiredParam/:optionalParam?') + .disableHttpSecurity() + .addQueryParameters({ name: 'optionalQuery', required: false }, { name: 'requiredQuery', required: true }) + .setCommandFunction(async function (_context, _payload, parameter) { + // add your business logic here - return { - parameter, - } - }) + return { + parameter, + } + }) diff --git a/examples/hono-example/src/service/ping/v1/command/paramTest/schema.ts b/examples/hono-example/src/service/ping/v1/command/paramTest/schema.ts index 7c82817a0..4687756db 100644 --- a/examples/hono-example/src/service/ping/v1/command/paramTest/schema.ts +++ b/examples/hono-example/src/service/ping/v1/command/paramTest/schema.ts @@ -3,15 +3,15 @@ import { z } from 'zod' // define the input parameters export const pingV1ParamTestInputParameterSchema = extendApi( - z.object({ - optionalQuery: extendApi(z.string().optional(), { example: 'optional' }), - requiredQuery: extendApi(z.string(), { example: 'required' }), - requiredParam: extendApi(z.string(), { example: 'required_id' }), - optionalParam: extendApi(z.string().optional(), { example: 'optionalParam' }), - }), - { - title: 'paramTest input parameter schema', - }, + z.object({ + optionalQuery: extendApi(z.string().optional(), { example: 'optional' }), + requiredQuery: extendApi(z.string(), { example: 'required' }), + requiredParam: extendApi(z.string(), { example: 'required_id' }), + optionalParam: extendApi(z.string().optional(), { example: 'optionalParam' }), + }), + { + title: 'paramTest input parameter schema', + }, ) // define the input payload @@ -19,13 +19,13 @@ export const pingV1ParamTestInputPayloadSchema = extendApi(z.undefined(), { titl // define the output payload export const pingV1ParamTestOutputPayloadSchema = extendApi( - z.object({ - parameter: z.object({ - optionalQuery: extendApi(z.string().optional(), { example: 'optional' }), - requiredQuery: extendApi(z.string(), { example: 'required' }), - requiredParam: extendApi(z.string(), { example: 'required_id' }), - optionalParam: extendApi(z.string().optional(), { example: 'optionalParam' }), - }), - }), - { title: 'paramTest output payload schema' }, + z.object({ + parameter: z.object({ + optionalQuery: extendApi(z.string().optional(), { example: 'optional' }), + requiredQuery: extendApi(z.string(), { example: 'required' }), + requiredParam: extendApi(z.string(), { example: 'required_id' }), + optionalParam: extendApi(z.string().optional(), { example: 'optionalParam' }), + }), + }), + { title: 'paramTest output payload schema' }, ) diff --git a/examples/hono-example/src/service/ping/v1/command/paramTest/types.ts b/examples/hono-example/src/service/ping/v1/command/paramTest/types.ts index 1dbf7b13f..fb8cdeed2 100644 --- a/examples/hono-example/src/service/ping/v1/command/paramTest/types.ts +++ b/examples/hono-example/src/service/ping/v1/command/paramTest/types.ts @@ -1,9 +1,9 @@ import type { z } from 'zod' import type { - pingV1ParamTestInputParameterSchema, - pingV1ParamTestInputPayloadSchema, - pingV1ParamTestOutputPayloadSchema, + pingV1ParamTestInputParameterSchema, + pingV1ParamTestInputPayloadSchema, + pingV1ParamTestOutputPayloadSchema, } from './schema.js' export type PingV1ParamTestInputParameter = z.input diff --git a/examples/hono-example/src/service/ping/v1/command/ping/ping.test.ts b/examples/hono-example/src/service/ping/v1/command/ping/ping.test.ts index 5cfc5e951..f7005035d 100644 --- a/examples/hono-example/src/service/ping/v1/command/ping/ping.test.ts +++ b/examples/hono-example/src/service/ping/v1/command/ping/ping.test.ts @@ -6,32 +6,32 @@ import { pingCommandBuilder } from './pingCommandBuilder.js' import type { PingV1PingInputParameter, PingV1PingInputPayload } from './types.js' describe('service Ping version 1 - command ping', () => { - let sandbox = createSandbox() - beforeEach(() => { - sandbox = createSandbox() - }) + let sandbox = createSandbox() + beforeEach(() => { + sandbox = createSandbox() + }) - afterEach(() => { - sandbox.restore() - }) + afterEach(() => { + sandbox.restore() + }) - test('does not throw', async () => { - const service = await pingV1Service.getInstance(getEventBridgeMock(sandbox).mock, { - logger: getLoggerMock(sandbox).mock, - }) + test('does not throw', async () => { + const service = await pingV1Service.getInstance(getEventBridgeMock(sandbox).mock, { + logger: getLoggerMock(sandbox).mock, + }) - const ping = safeBind(pingCommandBuilder.getCommandFunction(), service) + const ping = safeBind(pingCommandBuilder.getCommandFunction(), service) - const payload: PingV1PingInputPayload = { ping: 'test' } + const payload: PingV1PingInputPayload = { ping: 'test' } - const parameter: PingV1PingInputParameter = { - query: 'myQuery', - } + const parameter: PingV1PingInputParameter = { + query: 'myQuery', + } - const context = pingCommandBuilder.getCommandContextMock(payload, parameter, sandbox) + const context = pingCommandBuilder.getCommandContextMock({ payload, parameter, sandbox }) - const result = await ping(context.mock, payload, parameter) + const result = await ping(context.mock, payload, parameter) - expect(result).toStrictEqual({ pong: 'test' }) - }) + expect(result).toStrictEqual({ pong: 'test' }) + }) }) diff --git a/examples/hono-example/src/service/ping/v1/command/ping/pingCommandBuilder.ts b/examples/hono-example/src/service/ping/v1/command/ping/pingCommandBuilder.ts index 90d333d0e..fdc364274 100644 --- a/examples/hono-example/src/service/ping/v1/command/ping/pingCommandBuilder.ts +++ b/examples/hono-example/src/service/ping/v1/command/ping/pingCommandBuilder.ts @@ -1,23 +1,23 @@ import { ServiceEvent } from '../../../../ServiceEvent.enum.js' import { pingV1ServiceBuilder } from '../../pingV1ServiceBuilder.js' import { - pingV1PingInputParameterSchema, - pingV1PingInputPayloadSchema, - pingV1PingOutputPayloadSchema, + pingV1PingInputParameterSchema, + pingV1PingInputPayloadSchema, + pingV1PingOutputPayloadSchema, } from './schema.js' export const pingCommandBuilder = pingV1ServiceBuilder - .getCommandBuilder('ping', 'the ping command exposed as http endpoint') - .setSuccessEventName(ServiceEvent.Pinged) - .addPayloadSchema(pingV1PingInputPayloadSchema) - .addParameterSchema(pingV1PingInputParameterSchema) - .addOutputSchema(pingV1PingOutputPayloadSchema) - .exposeAsHttpEndpoint('POST', 'ping') - .addQueryParameters({ name: 'query', required: false }) - .disableHttpSecurity() - .setCommandFunction(async function (_context, payload, _parameter) { - // add your business logic here - return { - pong: payload.ping, - } - }) + .getCommandBuilder('ping', 'the ping command exposed as http endpoint') + .setSuccessEventName(ServiceEvent.Pinged) + .addPayloadSchema(pingV1PingInputPayloadSchema) + .addParameterSchema(pingV1PingInputParameterSchema) + .addOutputSchema(pingV1PingOutputPayloadSchema) + .exposeAsHttpEndpoint('POST', 'ping') + .addQueryParameters({ name: 'query', required: false }) + .disableHttpSecurity() + .setCommandFunction(async function (_context, payload, _parameter) { + // add your business logic here + return { + pong: payload.ping, + } + }) diff --git a/examples/hono-example/src/service/ping/v1/command/ping/schema.ts b/examples/hono-example/src/service/ping/v1/command/ping/schema.ts index 6954e4264..208edc687 100644 --- a/examples/hono-example/src/service/ping/v1/command/ping/schema.ts +++ b/examples/hono-example/src/service/ping/v1/command/ping/schema.ts @@ -3,24 +3,24 @@ import { z } from 'zod' // define the input parameters export const pingV1PingInputParameterSchema = extendApi( - z.object({ - query: extendApi(z.string(), { title: 'a query parameter' }), - }), - { title: 'ping input parameter schema' }, + z.object({ + query: extendApi(z.string().optional(), { title: 'a query parameter' }), + }), + { title: 'ping input parameter schema' }, ) // define the input payload export const pingV1PingInputPayloadSchema = extendApi( - z.object({ - ping: extendApi(z.string(), { title: 'Ping input' }), - }), - { title: 'ping input payload schema' }, + z.object({ + ping: extendApi(z.string(), { title: 'Ping input' }), + }), + { title: 'ping input payload schema' }, ) // define the output payload export const pingV1PingOutputPayloadSchema = extendApi( - z.object({ - pong: extendApi(z.string(), { title: 'Pong output' }), - }), - { title: 'ping output payload schema' }, + z.object({ + pong: extendApi(z.string(), { title: 'Pong output' }), + }), + { title: 'ping output payload schema' }, ) diff --git a/examples/hono-example/src/service/ping/v1/command/ping/types.ts b/examples/hono-example/src/service/ping/v1/command/ping/types.ts index eacdcc1f8..db48417b5 100644 --- a/examples/hono-example/src/service/ping/v1/command/ping/types.ts +++ b/examples/hono-example/src/service/ping/v1/command/ping/types.ts @@ -1,9 +1,9 @@ import type { z } from 'zod' import type { - pingV1PingInputParameterSchema, - pingV1PingInputPayloadSchema, - pingV1PingOutputPayloadSchema, + pingV1PingInputParameterSchema, + pingV1PingInputPayloadSchema, + pingV1PingOutputPayloadSchema, } from './schema.js' export type PingV1PingInputParameter = z.input diff --git a/examples/hono-example/src/service/ping/v1/pingV1Service.test.ts b/examples/hono-example/src/service/ping/v1/pingV1Service.test.ts index 977f69d99..cfd4e45f4 100644 --- a/examples/hono-example/src/service/ping/v1/pingV1Service.test.ts +++ b/examples/hono-example/src/service/ping/v1/pingV1Service.test.ts @@ -1,11 +1,7 @@ import { pingV1Service as service } from './pingV1Service.js' describe('service ping version 1', () => { - it('has valid commands', () => { - service.validateCommandDefinitions() - }) - - it('has valid subscriptions', () => { - service.validateSubscriptionDefinitions() - }) + it('has valid configuration', () => { + service.testServiceSetup() + }) }) diff --git a/examples/hono-example/src/service/ping/v1/pingV1Service.ts b/examples/hono-example/src/service/ping/v1/pingV1Service.ts index 34e5bc023..ce13a33fd 100644 --- a/examples/hono-example/src/service/ping/v1/pingV1Service.ts +++ b/examples/hono-example/src/service/ping/v1/pingV1Service.ts @@ -12,14 +12,14 @@ import { logSubscriptionBuilder } from './subscription/log/index.js' // other service config should be done in ./pingServiceBuilder.ts file const commandDefinitions: CommandDefinitionList = [ - pingCommandBuilder.getDefinition(), - fooCommandBuilder.getDefinition(), - paramTestCommandBuilder.getDefinition(), - deleteCommandBuilder.getDefinition(), + pingCommandBuilder.getDefinition(), + fooCommandBuilder.getDefinition(), + paramTestCommandBuilder.getDefinition(), + deleteCommandBuilder.getDefinition(), ] const subscriptionDefinitions: SubscriptionDefinitionList = [logSubscriptionBuilder.getDefinition()] export const pingV1Service = pingV1ServiceBuilder - .addCommandDefinition(...commandDefinitions) - .addSubscriptionDefinition(...subscriptionDefinitions) + .addCommandDefinition(...commandDefinitions) + .addSubscriptionDefinition(...subscriptionDefinitions) diff --git a/examples/hono-example/src/service/ping/v1/pingV1ServiceBuilder.ts b/examples/hono-example/src/service/ping/v1/pingV1ServiceBuilder.ts index 7ad735f14..9233f2f37 100644 --- a/examples/hono-example/src/service/ping/v1/pingV1ServiceBuilder.ts +++ b/examples/hono-example/src/service/ping/v1/pingV1ServiceBuilder.ts @@ -5,12 +5,10 @@ import { generalPingServiceInfo } from '../generalPingServiceInfo.js' import { pingServiceV1ConfigSchema } from './pingServiceConfig.js' export const pingServiceInfo = { - serviceVersion: '1', - ...generalPingServiceInfo, + serviceVersion: '1', + ...generalPingServiceInfo, } as const satisfies ServiceInfoType // create a service builder instance and assign service config schema and default config. -export const pingV1ServiceBuilder = new ServiceBuilder(pingServiceInfo) - .setConfigSchema(pingServiceV1ConfigSchema) - .setDefaultConfig({}) +export const pingV1ServiceBuilder = new ServiceBuilder(pingServiceInfo).setConfigSchema(pingServiceV1ConfigSchema) diff --git a/examples/hono-example/src/service/ping/v1/subscription/log/log.test.ts b/examples/hono-example/src/service/ping/v1/subscription/log/log.test.ts index b03d3bed2..fccf9bc7c 100644 --- a/examples/hono-example/src/service/ping/v1/subscription/log/log.test.ts +++ b/examples/hono-example/src/service/ping/v1/subscription/log/log.test.ts @@ -6,41 +6,41 @@ import { logSubscriptionBuilder } from './logSubscriptionBuilder.js' import type { PingV1LogInputPayload } from './types.js' describe('service Ping version 1 - subscription log', () => { - let sandbox = createSandbox() - beforeEach(() => { - sandbox = createSandbox() - }) + let sandbox = createSandbox() + beforeEach(() => { + sandbox = createSandbox() + }) - afterEach(() => { - sandbox.restore() - }) + afterEach(() => { + sandbox.restore() + }) - test('does not throw', async () => { - // create a service instance to be bind to the subscription function - const service = await pingV1Service.getInstance(getEventBridgeMock(sandbox).mock, { - logger: getLoggerMock(sandbox).mock, - }) + test('does not throw', async () => { + // create a service instance to be bind to the subscription function + const service = await pingV1Service.getInstance(getEventBridgeMock(sandbox).mock, { + logger: getLoggerMock(sandbox).mock, + }) - // get the subscription function and bind to service instance to work properly - const log = safeBind(logSubscriptionBuilder.getSubscriptionFunction(), service) + // get the subscription function and bind to service instance to work properly + const log = safeBind(logSubscriptionBuilder.getSubscriptionFunction(), service) - // define the test input payload - const payload: PingV1LogInputPayload = { - pong: 'test', - } + // define the test input payload + const payload: PingV1LogInputPayload = { + pong: 'test', + } - // define the test input parameter - const parameter = undefined as unknown as Readonly + // define the test input parameter + const parameter = undefined as unknown as Readonly - // create a mock message with the expected input for the subscription function - const message = getCommandSuccessMessageMock(payload) + // create a mock message with the expected input for the subscription function + const message = getCommandSuccessMessageMock(payload) - // create a subscription context for the subscription function - const context = logSubscriptionBuilder.getSubscriptionContextMock(message, sandbox) + // create a subscription context for the subscription function + const context = logSubscriptionBuilder.getSubscriptionContextMock({ message, sandbox }) - // execute the subscription function - const result = await log(context.mock, payload, parameter) + // execute the subscription function + const result = await log(context.mock, payload, parameter) - expect(result).toBeUndefined() - }) + expect(result).toBeUndefined() + }) }) diff --git a/examples/hono-example/src/service/ping/v1/subscription/log/logSubscriptionBuilder.ts b/examples/hono-example/src/service/ping/v1/subscription/log/logSubscriptionBuilder.ts index 52153625a..9ff7c41f3 100644 --- a/examples/hono-example/src/service/ping/v1/subscription/log/logSubscriptionBuilder.ts +++ b/examples/hono-example/src/service/ping/v1/subscription/log/logSubscriptionBuilder.ts @@ -3,10 +3,10 @@ import { pingV1ServiceBuilder } from '../../pingV1ServiceBuilder.js' import { pingV1LogInputPayloadSchema } from './schema.js' export const logSubscriptionBuilder = pingV1ServiceBuilder - .getSubscriptionBuilder('log', 'logs the ping events') - .subscribeToEvent(ServiceEvent.Pinged) - .addPayloadSchema(pingV1LogInputPayloadSchema) - .setSubscriptionFunction(async function (context, payload, _parameter) { - // add your business logic here - context.logger.info(`there was a ping responded with ${payload.pong}`) - }) + .getSubscriptionBuilder('log', 'logs the ping events') + .subscribeToEvent(ServiceEvent.Pinged) + .addPayloadSchema(pingV1LogInputPayloadSchema) + .setSubscriptionFunction(async function (context, payload, _parameter) { + // add your business logic here + context.logger.info(`there was a ping responded with ${payload.pong}`) + }) diff --git a/examples/hono-example/src/service/ping/v1/subscription/log/schema.ts b/examples/hono-example/src/service/ping/v1/subscription/log/schema.ts index 8421eae80..b4b583ac8 100644 --- a/examples/hono-example/src/service/ping/v1/subscription/log/schema.ts +++ b/examples/hono-example/src/service/ping/v1/subscription/log/schema.ts @@ -3,8 +3,8 @@ import { z } from 'zod' // define the input payload export const pingV1LogInputPayloadSchema = extendApi( - z.object({ - pong: extendApi(z.string(), { title: 'Pong' }), - }), - { title: 'pong payload schema' }, + z.object({ + pong: extendApi(z.string(), { title: 'Pong' }), + }), + { title: 'pong payload schema' }, ) diff --git a/examples/hono-example/tsconfig.json b/examples/hono-example/tsconfig.json index a84ca294e..7d9abe7a2 100644 --- a/examples/hono-example/tsconfig.json +++ b/examples/hono-example/tsconfig.json @@ -1,11 +1,8 @@ { - "extends": "../../tsconfig.json", - - "compilerOptions": { - "outDir": "./build" - }, - "files": [ - "src/index.ts", - "fastify.d.ts" - ], -} \ No newline at end of file + "extends": "../../tsconfig.json", + + "compilerOptions": { + "outDir": "./build" + }, + "files": ["src/index.ts", "fastify.d.ts"] +} diff --git a/examples/kubernetes/package.json b/examples/kubernetes/package.json index 82f693c37..5d94a4129 100644 --- a/examples/kubernetes/package.json +++ b/examples/kubernetes/package.json @@ -1,39 +1,39 @@ { - "name": "@purista/kubernetes-example", - "version": "1.11.0", - "description": "example how to deploy a single service in kubernetes", - "homepage": "https://purista.dev", - "private": true, - "repository": { - "type": "git", - "url": "git@github.com:sebastianwessel/purista.git" - }, - "type": "module", - "author": "Sebastian Wessel", - "license": "ISC", - "main": "src/index.ts", - "engines": { - "node": ">=18.15" - }, - "scripts": { - "start": "tsx src/index.ts | pino-pretty", - "lint": "eslint . --ext .ts,.json --cache . --fix", - "test": "vitest" - }, - "devDependencies": { - "@types/node": "^20.11.17", - "pino-pretty": "^10.3.1", - "sinon": "^17.0.1", - "tsx": "^4.7.0", - "typescript": "^5.3.3", - "vitest": "^1.3.0" - }, - "dependencies": { - "@hono/node-server": "^1.8.0", - "@opentelemetry/exporter-trace-otlp-http": "^0.48.0", - "@purista/amqpbridge": "*", - "@purista/core": "*", - "@purista/k8s-sdk": "*", - "zod": "^3.22.4" - } + "name": "@purista/kubernetes-example", + "version": "1.11.0", + "description": "example how to deploy a single service in kubernetes", + "homepage": "https://purista.dev", + "private": true, + "repository": { + "type": "git", + "url": "git@github.com:puristajs/purista.git" + }, + "type": "module", + "author": "Sebastian Wessel", + "license": "ISC", + "main": "src/index.ts", + "engines": { + "node": ">=18.15" + }, + "scripts": { + "start": "tsx src/index.ts | pino-pretty", + "lint": "npx @biomejs/biome check --write", + "test": "vitest" + }, + "devDependencies": { + "@types/node": "^22.5.1", + "pino-pretty": "^13.0.0", + "sinon": "^19.0.2", + "tsx": "^4.19.0", + "typescript": "^5.5.4", + "vitest": "^3.0.4" + }, + "dependencies": { + "@hono/node-server": "^1.12.2", + "@opentelemetry/exporter-trace-otlp-http": "^0.57.1", + "@purista/amqpbridge": "*", + "@purista/core": "*", + "@purista/k8s-sdk": "*", + "zod": "^3.24.1" + } } diff --git a/examples/kubernetes/src/index.ts b/examples/kubernetes/src/index.ts index dca6d346a..b3cae8aa6 100644 --- a/examples/kubernetes/src/index.ts +++ b/examples/kubernetes/src/index.ts @@ -3,98 +3,98 @@ import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http' import { SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base' import { AmqpBridge } from '@purista/amqpbridge' import { - DefaultConfigStore, - DefaultSecretStore, - DefaultStateStore, - getNewInstanceId, - gracefulShutdown, - initLogger, - UnhandledError, + DefaultConfigStore, + DefaultSecretStore, + DefaultStateStore, + UnhandledError, + getNewInstanceId, + gracefulShutdown, + initLogger, } from '@purista/core' import { getHttpServer } from '@purista/k8s-sdk' import { theServiceV1Service } from './service/theService/v1/index.js' const main = async () => { - // create a logger - const logger = initLogger('debug') + // create a logger + const logger = initLogger('debug') - // add listeners to log really unexpected errors - process.on('uncaughtException', (error, origin) => { - const err = UnhandledError.fromError(error) - logger.error({ err, origin }, `unhandled error: ${err.message}`) - }) + // add listeners to log really unexpected errors + process.on('uncaughtException', (error, origin) => { + const err = UnhandledError.fromError(error) + logger.error({ err, origin }, `unhandled error: ${err.message}`) + }) - process.on('unhandledRejection', (error, origin) => { - const err = UnhandledError.fromError(error) - logger.error({ err, origin }, `unhandled rejection: ${err.message}`) - }) + process.on('unhandledRejection', (error, origin) => { + const err = UnhandledError.fromError(error) + logger.error({ err, origin }, `unhandled rejection: ${err.message}`) + }) - // optional: set up opentelemetry if you like to use it - const exporter = new OTLPTraceExporter({ - url: `http://localhost:14268/api/traces`, - }) - const spanProcessor = new SimpleSpanProcessor(exporter) + // optional: set up opentelemetry if you like to use it + const exporter = new OTLPTraceExporter({ + url: 'http://localhost:14268/api/traces', + }) + const spanProcessor = new SimpleSpanProcessor(exporter) - // optional: set up stores if they are needed for your service - const secretStore = new DefaultSecretStore({ logger }) - const configStore = new DefaultConfigStore({ logger }) - const stateStore = new DefaultStateStore({ logger }) + // optional: set up stores if they are needed for your service + const secretStore = new DefaultSecretStore({ logger }) + const configStore = new DefaultConfigStore({ logger }) + const stateStore = new DefaultStateStore({ logger }) - // set up the eventbridge and start the event bridge - const eventBridge = new AmqpBridge({ - spanProcessor, - instanceId: process.env.HOSTNAME ?? getNewInstanceId(), - url: process.env.AMQP_URL, - }) - await eventBridge.start() + // set up the eventbridge and start the event bridge + const eventBridge = new AmqpBridge({ + spanProcessor, + instanceId: process.env.HOSTNAME ?? getNewInstanceId(), + url: process.env.AMQP_URL, + }) + await eventBridge.start() - // set up the service - const theService = await theServiceV1Service.getInstance(eventBridge, { - spanProcessor, - configStore, - secretStore, - stateStore, - }) - await theService.start() + // set up the service + const theService = await theServiceV1Service.getInstance(eventBridge, { + spanProcessor, + configStore, + secretStore, + stateStore, + }) + await theService.start() - // create http server - const app = getHttpServer({ - logger, - // check event bridge health if /healthz endpoint is called - healthFn: () => eventBridge.isHealthy(), - // optional: expose the commands if they are defined to have url endpoint - services: theService, - // optional: expose service endpoints at [apiMountPath]/v[serviceVersion]/[path defined for command] - // defaults to /api - apiMountPath: '/api', - }) + // create http server + const app = getHttpServer({ + logger, + // check event bridge health if /healthz endpoint is called + healthFn: () => eventBridge.isHealthy(), + // optional: expose the commands if they are defined to have url endpoint + services: theService, + // optional: expose service endpoints at [apiMountPath]/v[serviceVersion]/[path defined for command] + // defaults to /api + apiMountPath: '/api', + }) - // start the http server - // defaults to port 3000 - // optional: you can set the port in the optional parameter of this method - const server = serve({ - fetch: app.fetch, - }) + // start the http server + // defaults to port 3000 + // optional: you can set the port in the optional parameter of this method + const server = serve({ + fetch: app.fetch, + }) - // register shut down methods - gracefulShutdown(logger, [ - // begin with the event bridge to no longer accept incoming messages - eventBridge, - // optional: shut down the service - theService, - // optional: shut down the secret store - secretStore, - // optional: shut down the config store - configStore, - // optional: shut down the state store - stateStore, - { - name: 'httpserver', - destroy: async () => - new Promise((resolve, reject) => server.close((err) => (err ? reject(err) : resolve(undefined)))), - }, - ]) + // register shut down methods + gracefulShutdown(logger, [ + // begin with the event bridge to no longer accept incoming messages + eventBridge, + // optional: shut down the service + theService, + // optional: shut down the secret store + secretStore, + // optional: shut down the config store + configStore, + // optional: shut down the state store + stateStore, + { + name: 'httpserver', + destroy: async () => + new Promise((resolve, reject) => server.close(err => (err ? reject(err) : resolve(undefined)))), + }, + ]) } main() diff --git a/examples/kubernetes/src/service/ServiceEvent.enum.ts b/examples/kubernetes/src/service/ServiceEvent.enum.ts index f482f1a9a..a94873486 100644 --- a/examples/kubernetes/src/service/ServiceEvent.enum.ts +++ b/examples/kubernetes/src/service/ServiceEvent.enum.ts @@ -1,7 +1,7 @@ export enum ServiceEvent { - /** - * Emitted by theService v1 command ping: - * provide a dummy command - */ - Pinged = 'pinged', + /** + * Emitted by theService v1 command ping: + * provide a dummy command + */ + Pinged = 'pinged', } diff --git a/examples/kubernetes/src/service/theService/generalTheServiceServiceInfo.ts b/examples/kubernetes/src/service/theService/generalTheServiceServiceInfo.ts index 4fa910479..a1c46a051 100644 --- a/examples/kubernetes/src/service/theService/generalTheServiceServiceInfo.ts +++ b/examples/kubernetes/src/service/theService/generalTheServiceServiceInfo.ts @@ -1,6 +1,6 @@ import type { ServiceInfoType } from '@purista/core' export const generalTheServiceServiceInfo = { - serviceName: 'TheService', - serviceDescription: 'a example service', + serviceName: 'TheService', + serviceDescription: 'a example service', } as const satisfies Omit diff --git a/examples/kubernetes/src/service/theService/v1/command/ping/ping.test.ts b/examples/kubernetes/src/service/theService/v1/command/ping/ping.test.ts index edb36ec11..66cf7fb2a 100644 --- a/examples/kubernetes/src/service/theService/v1/command/ping/ping.test.ts +++ b/examples/kubernetes/src/service/theService/v1/command/ping/ping.test.ts @@ -6,30 +6,30 @@ import { pingCommandBuilder } from './pingCommandBuilder.js' import type { TheServiceV1PingInputParameter, TheServiceV1PingInputPayload } from './types.js' describe('service TheService version 1 - command ping', () => { - let sandbox = createSandbox() - beforeEach(() => { - sandbox = createSandbox() - }) + let sandbox = createSandbox() + beforeEach(() => { + sandbox = createSandbox() + }) - afterEach(() => { - sandbox.restore() - }) + afterEach(() => { + sandbox.restore() + }) - test('does not throw', async () => { - const service = await theServiceV1Service.getInstance(getEventBridgeMock(sandbox).mock, { - logger: getLoggerMock(sandbox).mock, - }) + test('does not throw', async () => { + const service = await theServiceV1Service.getInstance(getEventBridgeMock(sandbox).mock, { + logger: getLoggerMock(sandbox).mock, + }) - const ping = safeBind(pingCommandBuilder.getCommandFunction(), service) + const ping = safeBind(pingCommandBuilder.getCommandFunction(), service) - const payload: TheServiceV1PingInputPayload = undefined + const payload: TheServiceV1PingInputPayload = undefined - const parameter: TheServiceV1PingInputParameter = {} + const parameter: TheServiceV1PingInputParameter = {} - const context = pingCommandBuilder.getCommandContextMock(payload, parameter, sandbox) + const context = pingCommandBuilder.getCommandContextMock({ payload, parameter, sandbox }) - const result = await ping(context.mock, payload, parameter) + const result = await ping(context.mock, payload, parameter) - expect(result).toEqual({ ping: true }) - }) + expect(result).toEqual({ ping: true }) + }) }) diff --git a/examples/kubernetes/src/service/theService/v1/command/ping/pingCommandBuilder.ts b/examples/kubernetes/src/service/theService/v1/command/ping/pingCommandBuilder.ts index f3e6d94c4..b0bb28312 100644 --- a/examples/kubernetes/src/service/theService/v1/command/ping/pingCommandBuilder.ts +++ b/examples/kubernetes/src/service/theService/v1/command/ping/pingCommandBuilder.ts @@ -1,20 +1,20 @@ import { ServiceEvent } from '../../../../ServiceEvent.enum.js' import { theServiceServiceBuilder } from '../../theServiceServiceBuilder.js' import { - theServiceV1PingInputParameterSchema, - theServiceV1PingInputPayloadSchema, - theServiceV1PingOutputPayloadSchema, + theServiceV1PingInputParameterSchema, + theServiceV1PingInputPayloadSchema, + theServiceV1PingOutputPayloadSchema, } from './schema.js' export const pingCommandBuilder = theServiceServiceBuilder - .getCommandBuilder('ping', 'provide a dummy command') - .setSuccessEventName(ServiceEvent.Pinged) - .addPayloadSchema(theServiceV1PingInputPayloadSchema) - .addParameterSchema(theServiceV1PingInputParameterSchema) - .addOutputSchema(theServiceV1PingOutputPayloadSchema) - .exposeAsHttpEndpoint('GET', 'ping') - .setCommandFunction(async function (_context, _payload, _parameter) { - return { - ping: true, - } - }) + .getCommandBuilder('ping', 'provide a dummy command') + .setSuccessEventName(ServiceEvent.Pinged) + .addPayloadSchema(theServiceV1PingInputPayloadSchema) + .addParameterSchema(theServiceV1PingInputParameterSchema) + .addOutputSchema(theServiceV1PingOutputPayloadSchema) + .exposeAsHttpEndpoint('GET', 'ping') + .setCommandFunction(async function (_context, _payload, _parameter) { + return { + ping: true, + } + }) diff --git a/examples/kubernetes/src/service/theService/v1/command/ping/schema.ts b/examples/kubernetes/src/service/theService/v1/command/ping/schema.ts index a23d6298b..8b92a8cb9 100644 --- a/examples/kubernetes/src/service/theService/v1/command/ping/schema.ts +++ b/examples/kubernetes/src/service/theService/v1/command/ping/schema.ts @@ -9,5 +9,5 @@ export const theServiceV1PingInputPayloadSchema = extendApi(z.undefined(), { tit // define the output payload export const theServiceV1PingOutputPayloadSchema = extendApi(z.object({ ping: z.boolean() }), { - title: 'ping output payload schema', + title: 'ping output payload schema', }) diff --git a/examples/kubernetes/src/service/theService/v1/command/ping/types.ts b/examples/kubernetes/src/service/theService/v1/command/ping/types.ts index babb58f4b..1aacbf07a 100644 --- a/examples/kubernetes/src/service/theService/v1/command/ping/types.ts +++ b/examples/kubernetes/src/service/theService/v1/command/ping/types.ts @@ -1,9 +1,9 @@ import type { z } from 'zod' import type { - theServiceV1PingInputParameterSchema, - theServiceV1PingInputPayloadSchema, - theServiceV1PingOutputPayloadSchema, + theServiceV1PingInputParameterSchema, + theServiceV1PingInputPayloadSchema, + theServiceV1PingOutputPayloadSchema, } from './schema.js' export type TheServiceV1PingInputParameter = z.input diff --git a/examples/kubernetes/src/service/theService/v1/theServiceServiceBuilder.ts b/examples/kubernetes/src/service/theService/v1/theServiceServiceBuilder.ts index 3a0b86235..ea7eb8dee 100644 --- a/examples/kubernetes/src/service/theService/v1/theServiceServiceBuilder.ts +++ b/examples/kubernetes/src/service/theService/v1/theServiceServiceBuilder.ts @@ -5,12 +5,12 @@ import { generalTheServiceServiceInfo } from '../generalTheServiceServiceInfo.js import { theServiceServiceV1ConfigSchema } from './theServiceServiceConfig.js' export const theServiceServiceInfo = { - serviceVersion: '1', - ...generalTheServiceServiceInfo, + serviceVersion: '1', + ...generalTheServiceServiceInfo, } as const satisfies ServiceInfoType // create a service builder instance and assign service config schema and default config. -export const theServiceServiceBuilder = new ServiceBuilder(theServiceServiceInfo) - .setConfigSchema(theServiceServiceV1ConfigSchema) - .setDefaultConfig({}) +export const theServiceServiceBuilder = new ServiceBuilder(theServiceServiceInfo).setConfigSchema( + theServiceServiceV1ConfigSchema, +) diff --git a/examples/kubernetes/src/service/theService/v1/theServiceV1Service.test.ts b/examples/kubernetes/src/service/theService/v1/theServiceV1Service.test.ts index 503a038b9..276a51116 100644 --- a/examples/kubernetes/src/service/theService/v1/theServiceV1Service.test.ts +++ b/examples/kubernetes/src/service/theService/v1/theServiceV1Service.test.ts @@ -1,11 +1,7 @@ import { theServiceV1Service as service } from './theServiceV1Service.js' describe('service theService version 1', () => { - it('has valid commands', () => { - service.validateCommandDefinitions() - }) - - it('has valid subscriptions', () => { - service.validateSubscriptionDefinitions() - }) + it('has valid configuration', () => { + service.testServiceSetup() + }) }) diff --git a/examples/kubernetes/src/service/theService/v1/theServiceV1Service.ts b/examples/kubernetes/src/service/theService/v1/theServiceV1Service.ts index e2b4d7b1f..013eb1a05 100644 --- a/examples/kubernetes/src/service/theService/v1/theServiceV1Service.ts +++ b/examples/kubernetes/src/service/theService/v1/theServiceV1Service.ts @@ -12,5 +12,5 @@ const commandDefinitions: CommandDefinitionList = [pingCommandBuilder.getDe const subscriptionDefinitions: SubscriptionDefinitionList = [] export const theServiceV1Service = theServiceServiceBuilder - .addCommandDefinition(...commandDefinitions) - .addSubscriptionDefinition(...subscriptionDefinitions) + .addCommandDefinition(...commandDefinitions) + .addSubscriptionDefinition(...subscriptionDefinitions) diff --git a/examples/kubernetes/tsconfig.json b/examples/kubernetes/tsconfig.json index a36e69a7f..0eefbf7c8 100644 --- a/examples/kubernetes/tsconfig.json +++ b/examples/kubernetes/tsconfig.json @@ -1,11 +1,8 @@ { - "extends": "../../tsconfig.json", - - "compilerOptions": { - "outDir": "./build" - }, - "include": [ - "src/**/*", - "./test/*", - ] -} \ No newline at end of file + "extends": "../../tsconfig.json", + + "compilerOptions": { + "outDir": "./build" + }, + "include": ["src/**/*", "./test/*"] +} diff --git a/examples/mqtt-bridge/package.json b/examples/mqtt-bridge/package.json index 3473c4337..d228d76e5 100644 --- a/examples/mqtt-bridge/package.json +++ b/examples/mqtt-bridge/package.json @@ -1,40 +1,40 @@ { - "name": "@purista/mqtt-example", - "version": "1.11.0", - "description": "purista backend framework", - "homepage": "https://purista.dev", - "private": true, - "repository": { - "type": "git", - "url": "git@github.com:sebastianwessel/purista.git" - }, - "author": "Sebastian Wessel", - "license": "ISC", - "main": "src/index.ts", - "type": "module", - "engines": { - "node": ">=18.15" - }, - "scripts": { - "start": "tsx src/index.ts | pino-pretty", - "mqtt:up": "docker start purista-mqtt || docker run -p 1883:1883 -p 9001:9001 -d -v ./mosquitto.conf:/mosquitto/config/mosquitto.conf --name purista-mqtt eclipse-mosquitto", - "mqtt:down": "docker container stop $(docker container ls -q --filter name=purista-mqtt)", - "lint": "eslint . --ext .ts,.json --cache . --fix", - "test": "vitest" - }, - "devDependencies": { - "@types/node": "^20.11.17", - "pino-pretty": "^10.3.1", - "sinon": "^17.0.1", - "tsx": "^4.7.0", - "typescript": "^5.3.3", - "vitest": "^1.3.0" - }, - "dependencies": { - "@fastify/static": "^7.0.1", - "@purista/core": "*", - "@purista/httpserver": "*", - "@purista/mqttbridge": "*", - "zod": "3.22.4" - } + "name": "@purista/mqtt-example", + "version": "1.11.0", + "description": "purista backend framework", + "homepage": "https://purista.dev", + "private": true, + "repository": { + "type": "git", + "url": "git@github.com:puristajs/purista.git" + }, + "author": "Sebastian Wessel", + "license": "ISC", + "main": "src/index.ts", + "type": "module", + "engines": { + "node": ">=18.15" + }, + "scripts": { + "start": "tsx src/index.ts | pino-pretty", + "mqtt:up": "docker start purista-mqtt || docker run -p 1883:1883 -p 9001:9001 -d -v ./mosquitto.conf:/mosquitto/config/mosquitto.conf --name purista-mqtt eclipse-mosquitto", + "mqtt:down": "docker container stop $(docker container ls -q --filter name=purista-mqtt)", + "lint": "npx @biomejs/biome check --write", + "test": "vitest" + }, + "devDependencies": { + "@types/node": "^22.5.1", + "pino-pretty": "^13.0.0", + "sinon": "^19.0.2", + "tsx": "^4.19.0", + "typescript": "^5.5.4", + "vitest": "^3.0.4" + }, + "dependencies": { + "@fastify/static": "^8.0.3", + "@purista/core": "*", + "@purista/httpserver": "*", + "@purista/mqttbridge": "*", + "zod": "^3.24.1" + } } diff --git a/examples/mqtt-bridge/src/config/httpServerConfig.ts b/examples/mqtt-bridge/src/config/httpServerConfig.ts index 4ad70a945..89808c616 100644 --- a/examples/mqtt-bridge/src/config/httpServerConfig.ts +++ b/examples/mqtt-bridge/src/config/httpServerConfig.ts @@ -1,20 +1,20 @@ import type { HttpServerServiceV1Config } from '@purista/httpserver' const httpServerConfig: HttpServerServiceV1Config = { - fastify: {}, - port: 8080, - logLevel: 'debug', - domain: 'localhost', - apiMountPath: '/api', - enableCors: false, - openApi: { - enabled: true, - info: { - title: 'backend api', - description: 'OpenApi definition for server endpoints', - version: '1.0.0', - }, - }, + fastify: {}, + port: 8080, + logLevel: 'debug', + domain: 'localhost', + apiMountPath: '/api', + enableCors: false, + openApi: { + enabled: true, + info: { + title: 'backend api', + description: 'OpenApi definition for server endpoints', + version: '1.0.0', + }, + }, } export default httpServerConfig diff --git a/examples/mqtt-bridge/src/main.ts b/examples/mqtt-bridge/src/main.ts index c34d621b6..6cf05056c 100644 --- a/examples/mqtt-bridge/src/main.ts +++ b/examples/mqtt-bridge/src/main.ts @@ -10,52 +10,52 @@ import { emailV1Service } from './service/email/v1/index.js' import { userV1Service } from './service/user/v1/index.js' export const main = async () => { - // initialize the logging - const logger = initLogger('debug') - - logger.info('application starts') - - // create and init our eventbridge - const eventBridge = new MqttBridge({ - logger, - host: 'localhost', - }) - await eventBridge.start() - - // create a state store - const stateStore = new DefaultStateStore({ logger }) - - // create and init a webserver - const httpServerService = await httpServerV1Service.getInstance(eventBridge, { - serviceConfig: httpServerConfig, - }) - - const defaultPublicPath = resolve(__dirname, '..', 'public') - - // static file handler - httpServerService.server?.register(fastifyStatic, { - root: defaultPublicPath, - decorateReply: false, - }) - - // start the webserver - await httpServerService.start() - - const userService = await userV1Service.getInstance(eventBridge, { logger, stateStore }) - await userService.start() - - const emailService = await emailV1Service.getInstance(eventBridge, { logger, stateStore }) - await emailService.start() - - logger.info('application ready') - logger.info(`open in browser: http://localhost:${httpServerConfig.port}`) - - gracefulShutdown(logger, [ - // begin with the event bridge to no longer accept incoming messages - eventBridge, - userService, - emailService, - stateStore, - httpServerService, - ]) + // initialize the logging + const logger = initLogger('debug') + + logger.info('application starts') + + // create and init our eventbridge + const eventBridge = new MqttBridge({ + logger, + host: 'localhost', + }) + await eventBridge.start() + + // create a state store + const stateStore = new DefaultStateStore({ logger }) + + // create and init a webserver + const httpServerService = await httpServerV1Service.getInstance(eventBridge, { + serviceConfig: httpServerConfig, + }) + + const defaultPublicPath = resolve(__dirname, '..', 'public') + + // static file handler + httpServerService.server?.register(fastifyStatic, { + root: defaultPublicPath, + decorateReply: false, + }) + + // start the webserver + await httpServerService.start() + + const userService = await userV1Service.getInstance(eventBridge, { logger, stateStore }) + await userService.start() + + const emailService = await emailV1Service.getInstance(eventBridge, { logger, stateStore }) + await emailService.start() + + logger.info('application ready') + logger.info(`open in browser: http://localhost:${httpServerConfig.port}`) + + gracefulShutdown(logger, [ + // begin with the event bridge to no longer accept incoming messages + eventBridge, + userService, + emailService, + stateStore, + httpServerService, + ]) } diff --git a/examples/mqtt-bridge/src/service/ServiceEvent.enum.ts b/examples/mqtt-bridge/src/service/ServiceEvent.enum.ts index 5673fb208..a9103a39b 100644 --- a/examples/mqtt-bridge/src/service/ServiceEvent.enum.ts +++ b/examples/mqtt-bridge/src/service/ServiceEvent.enum.ts @@ -1,12 +1,12 @@ export enum ServiceEvent { - /** - * Emitted by user v1 command signUp: - * a new user registration - */ - NewUserRegistered = 'newUserRegistered', - /** - * Emitted by email v1 subscription sendWelcomeEmail: - * a new user registration - */ - WelcomeEmailSent = 'send a welcome mail to new registered users', + /** + * Emitted by user v1 command signUp: + * a new user registration + */ + NewUserRegistered = 'newUserRegistered', + /** + * Emitted by email v1 subscription sendWelcomeEmail: + * a new user registration + */ + WelcomeEmailSent = 'send a welcome mail to new registered users', } diff --git a/examples/mqtt-bridge/src/service/email/generalEmailServiceInfo.ts b/examples/mqtt-bridge/src/service/email/generalEmailServiceInfo.ts index c8cbc5538..446d54e53 100644 --- a/examples/mqtt-bridge/src/service/email/generalEmailServiceInfo.ts +++ b/examples/mqtt-bridge/src/service/email/generalEmailServiceInfo.ts @@ -1,6 +1,6 @@ import type { ServiceInfoType } from '@purista/core' export const generalEmailServiceInfo = { - serviceName: 'Email', - serviceDescription: 'sends emails to customers', + serviceName: 'Email', + serviceDescription: 'sends emails to customers', } as const satisfies Omit diff --git a/examples/mqtt-bridge/src/service/email/v1/emailV1Service.test.ts b/examples/mqtt-bridge/src/service/email/v1/emailV1Service.test.ts index 5b9b12e07..f60972446 100644 --- a/examples/mqtt-bridge/src/service/email/v1/emailV1Service.test.ts +++ b/examples/mqtt-bridge/src/service/email/v1/emailV1Service.test.ts @@ -1,11 +1,7 @@ import { emailV1Service as service } from './emailV1Service.js' describe('service email version 1', () => { - it('has valid commands', () => { - service.validateCommandDefinitions() - }) - - it('has valid subscriptions', () => { - service.validateSubscriptionDefinitions() - }) + it('has valid setup', () => { + service.testServiceSetup() + }) }) diff --git a/examples/mqtt-bridge/src/service/email/v1/emailV1Service.ts b/examples/mqtt-bridge/src/service/email/v1/emailV1Service.ts index 6b8cdf599..8535f9ba3 100644 --- a/examples/mqtt-bridge/src/service/email/v1/emailV1Service.ts +++ b/examples/mqtt-bridge/src/service/email/v1/emailV1Service.ts @@ -12,5 +12,5 @@ const commandDefinitions: CommandDefinitionList = [] const subscriptionDefinitions: SubscriptionDefinitionList = [sendWelcomeEmailSubscriptionBuilder.getDefinition()] export const emailV1Service = emailV1ServiceBuilder - .addCommandDefinition(...commandDefinitions) - .addSubscriptionDefinition(...subscriptionDefinitions) + .addCommandDefinition(...commandDefinitions) + .addSubscriptionDefinition(...subscriptionDefinitions) diff --git a/examples/mqtt-bridge/src/service/email/v1/emailV1ServiceBuilder.ts b/examples/mqtt-bridge/src/service/email/v1/emailV1ServiceBuilder.ts index ab2fd3b9a..926b0389e 100644 --- a/examples/mqtt-bridge/src/service/email/v1/emailV1ServiceBuilder.ts +++ b/examples/mqtt-bridge/src/service/email/v1/emailV1ServiceBuilder.ts @@ -5,12 +5,10 @@ import { generalEmailServiceInfo } from '../generalEmailServiceInfo.js' import { emailServiceV1ConfigSchema } from './emailServiceConfig.js' export const emailServiceInfo = { - serviceVersion: '1', - ...generalEmailServiceInfo, + serviceVersion: '1', + ...generalEmailServiceInfo, } as const satisfies ServiceInfoType // create a service builder instance and assign service config schema and default config. -export const emailV1ServiceBuilder = new ServiceBuilder(emailServiceInfo) - .setConfigSchema(emailServiceV1ConfigSchema) - .setDefaultConfig({}) +export const emailV1ServiceBuilder = new ServiceBuilder(emailServiceInfo).setConfigSchema(emailServiceV1ConfigSchema) diff --git a/examples/mqtt-bridge/src/service/email/v1/subscription/sendWelcomeEmail/schema.ts b/examples/mqtt-bridge/src/service/email/v1/subscription/sendWelcomeEmail/schema.ts index 97bd10475..227f140eb 100644 --- a/examples/mqtt-bridge/src/service/email/v1/subscription/sendWelcomeEmail/schema.ts +++ b/examples/mqtt-bridge/src/service/email/v1/subscription/sendWelcomeEmail/schema.ts @@ -2,9 +2,9 @@ import { z } from 'zod' // define the input payload export const emailV1SendWelcomeEmailInputPayloadSchema = z.object({ - userId: z.string().uuid(), + userId: z.string().uuid(), }) export const emailV1SendWelcomeEmailOutputPayloadSchema = z.object({ - userId: z.string().uuid(), + userId: z.string().uuid(), }) diff --git a/examples/mqtt-bridge/src/service/email/v1/subscription/sendWelcomeEmail/sendWelcomeEmail.test.ts b/examples/mqtt-bridge/src/service/email/v1/subscription/sendWelcomeEmail/sendWelcomeEmail.test.ts index 1e0f8fb27..20ad177a5 100644 --- a/examples/mqtt-bridge/src/service/email/v1/subscription/sendWelcomeEmail/sendWelcomeEmail.test.ts +++ b/examples/mqtt-bridge/src/service/email/v1/subscription/sendWelcomeEmail/sendWelcomeEmail.test.ts @@ -7,54 +7,54 @@ import { sendWelcomeEmailSubscriptionBuilder } from './sendWelcomeEmailSubscript import type { EmailV1SendWelcomeEmailInputPayload } from './types.js' describe('service Email version 1 - subscription sendWelcomeEmail', () => { - let sandbox = createSandbox() - beforeEach(() => { - sandbox = createSandbox() - }) - - afterEach(() => { - sandbox.restore() - }) - - test('sends an email', async () => { - // create a service instance to be bind to the subscription function - const service = await emailV1Service.getInstance(getEventBridgeMock(sandbox).mock, { - logger: getLoggerMock(sandbox).mock, - }) - - // get the subscription function and bind to service instance to work properly - const sendWelcomeEmail = safeBind(sendWelcomeEmailSubscriptionBuilder.getSubscriptionFunction(), service) - - const userMock: User = { - email: 'email@example.com', - name: 'test user', - password: 'password', - userId: 'a5fef052-911c-472c-ac25-e2da327f0af5', - } - - // define the test input payload - const payload: EmailV1SendWelcomeEmailInputPayload = { userId: userMock.userId } - - // define the test input parameter - const parameter = undefined as unknown as Readonly - - // create a mock message with the expected input for the subscription function - const message = getCommandSuccessMessageMock(payload) - - // create a subscription context for the subscription function - const context = sendWelcomeEmailSubscriptionBuilder.getSubscriptionContextMock(message, sandbox) - - context.stubs.service.User['1'].getUserById.resolves(userMock) - context.stubs.getConfig.resolves({ emailProviderUrl: 'https://example.com' }) - context.stubs.getSecret.resolves({ emailProviderAuthToken: 'secret_token' }) - - // execute the subscription function - const result = await sendWelcomeEmail(context.mock, payload, parameter) - - expect( - context.stubs.logger.debug.calledWith('Using email provider https://example.com with token secret_token'), - ).toBeTruthy() - expect(context.stubs.logger.info.calledWith('Welcome email to user sent to ' + userMock.email)).toBeTruthy() - expect(result).toStrictEqual({ userId: userMock.userId }) - }) + let sandbox = createSandbox() + beforeEach(() => { + sandbox = createSandbox() + }) + + afterEach(() => { + sandbox.restore() + }) + + test('sends an email', async () => { + // create a service instance to be bind to the subscription function + const service = await emailV1Service.getInstance(getEventBridgeMock(sandbox).mock, { + logger: getLoggerMock(sandbox).mock, + }) + + // get the subscription function and bind to service instance to work properly + const sendWelcomeEmail = safeBind(sendWelcomeEmailSubscriptionBuilder.getSubscriptionFunction(), service) + + const userMock: User = { + email: 'email@example.com', + name: 'test user', + password: 'password', + userId: 'a5fef052-911c-472c-ac25-e2da327f0af5', + } + + // define the test input payload + const payload: EmailV1SendWelcomeEmailInputPayload = { userId: userMock.userId } + + // define the test input parameter + const parameter = undefined as unknown as Readonly + + // create a mock message with the expected input for the subscription function + const message = getCommandSuccessMessageMock(payload) + + // create a subscription context for the subscription function + const context = sendWelcomeEmailSubscriptionBuilder.getSubscriptionContextMock({ message, sandbox }) + + context.stubs.service.User['1'].getUserById.resolves(userMock) + context.stubs.getConfig.resolves({ emailProviderUrl: 'https://example.com' }) + context.stubs.getSecret.resolves({ emailProviderAuthToken: 'secret_token' }) + + // execute the subscription function + const result = await sendWelcomeEmail(context.mock, payload, parameter) + + expect( + context.stubs.logger.debug.calledWith('Using email provider https://example.com with token secret_token'), + ).toBeTruthy() + expect(context.stubs.logger.info.calledWith(`Welcome email to user sent to ${userMock.email}`)).toBeTruthy() + expect(result).toStrictEqual({ userId: userMock.userId }) + }) }) diff --git a/examples/mqtt-bridge/src/service/email/v1/subscription/sendWelcomeEmail/sendWelcomeEmailSubscriptionBuilder.ts b/examples/mqtt-bridge/src/service/email/v1/subscription/sendWelcomeEmail/sendWelcomeEmailSubscriptionBuilder.ts index f516ad07f..ebcab8f02 100644 --- a/examples/mqtt-bridge/src/service/email/v1/subscription/sendWelcomeEmail/sendWelcomeEmailSubscriptionBuilder.ts +++ b/examples/mqtt-bridge/src/service/email/v1/subscription/sendWelcomeEmail/sendWelcomeEmailSubscriptionBuilder.ts @@ -1,35 +1,35 @@ import { ServiceEvent } from '../../../../ServiceEvent.enum.js' import { - userV1GetUserByIdInputParameterSchema, - userV1GetUserByIdInputPayloadSchema, - userV1GetUserByIdOutputPayloadSchema, + userV1GetUserByIdInputParameterSchema, + userV1GetUserByIdInputPayloadSchema, + userV1GetUserByIdOutputPayloadSchema, } from '../../../../user/v1/command/getUserById/index.js' import { emailV1ServiceBuilder } from '../../emailV1ServiceBuilder.js' import { emailV1SendWelcomeEmailInputPayloadSchema, emailV1SendWelcomeEmailOutputPayloadSchema } from './schema.js' export const sendWelcomeEmailSubscriptionBuilder = emailV1ServiceBuilder - .getSubscriptionBuilder('sendWelcomeEmail', 'send a welcome mail to new registered users') - .subscribeToEvent(ServiceEvent.NewUserRegistered) - .addPayloadSchema(emailV1SendWelcomeEmailInputPayloadSchema) - .addOutputSchema(ServiceEvent.WelcomeEmailSent, emailV1SendWelcomeEmailOutputPayloadSchema) - .canInvoke( - 'User', - '1', - 'getUserById', - userV1GetUserByIdOutputPayloadSchema, - userV1GetUserByIdInputPayloadSchema, - userV1GetUserByIdInputParameterSchema, - ) - .setSubscriptionFunction(async function (context, payload, _parameter) { - const config = await context.configs.getConfig('emailProviderUrl') - const secrets = await context.secrets.getSecret('emailProviderAuthToken') + .getSubscriptionBuilder('sendWelcomeEmail', 'send a welcome mail to new registered users') + .subscribeToEvent(ServiceEvent.NewUserRegistered) + .addPayloadSchema(emailV1SendWelcomeEmailInputPayloadSchema) + .addOutputSchema(ServiceEvent.WelcomeEmailSent, emailV1SendWelcomeEmailOutputPayloadSchema) + .canInvoke( + 'User', + '1', + 'getUserById', + userV1GetUserByIdOutputPayloadSchema, + userV1GetUserByIdInputPayloadSchema, + userV1GetUserByIdInputParameterSchema, + ) + .setSubscriptionFunction(async function (context, payload, _parameter) { + const config = await context.configs.getConfig('emailProviderUrl') + const secrets = await context.secrets.getSecret('emailProviderAuthToken') - context.logger.debug(`Using email provider ${config.emailProviderUrl} with token ${secrets.emailProviderAuthToken}`) + context.logger.debug(`Using email provider ${config.emailProviderUrl} with token ${secrets.emailProviderAuthToken}`) - const user = await context.service.User['1'].getUserById(undefined, payload) + const user = await context.service.User['1'].getUserById(undefined, payload) - // add your business logic here - context.logger.info(`Welcome email to user sent to ${user.email}`) + // add your business logic here + context.logger.info(`Welcome email to user sent to ${user.email}`) - return payload - }) + return payload + }) diff --git a/examples/mqtt-bridge/src/service/user/generalUserServiceInfo.ts b/examples/mqtt-bridge/src/service/user/generalUserServiceInfo.ts index b5e2bafd7..770f5c84e 100644 --- a/examples/mqtt-bridge/src/service/user/generalUserServiceInfo.ts +++ b/examples/mqtt-bridge/src/service/user/generalUserServiceInfo.ts @@ -1,6 +1,6 @@ import type { ServiceInfoType } from '@purista/core' export const generalUserServiceInfo = { - serviceName: 'User', - serviceDescription: 'manage user information', + serviceName: 'User', + serviceDescription: 'manage user information', } as const satisfies Omit diff --git a/examples/mqtt-bridge/src/service/user/v1/command/getAllUsers/getAllUsers.test.ts b/examples/mqtt-bridge/src/service/user/v1/command/getAllUsers/getAllUsers.test.ts index 3917b01dd..2e1b8032e 100644 --- a/examples/mqtt-bridge/src/service/user/v1/command/getAllUsers/getAllUsers.test.ts +++ b/examples/mqtt-bridge/src/service/user/v1/command/getAllUsers/getAllUsers.test.ts @@ -8,41 +8,41 @@ import { getAllUsersCommandBuilder } from './getAllUsersCommandBuilder.js' import type { UserV1GetAllUsersInputParameter, UserV1GetAllUsersInputPayload } from './types.js' describe('service User version 1 - command getAllUsers', () => { - let sandbox = createSandbox() - beforeEach(() => { - sandbox = createSandbox() - }) + let sandbox = createSandbox() + beforeEach(() => { + sandbox = createSandbox() + }) - afterEach(() => { - sandbox.restore() - }) + afterEach(() => { + sandbox.restore() + }) - test('does not throw', async () => { - const service = await userV1Service.getInstance(getEventBridgeMock(sandbox).mock, { - logger: getLoggerMock(sandbox).mock, - }) + test('does not throw', async () => { + const service = await userV1Service.getInstance(getEventBridgeMock(sandbox).mock, { + logger: getLoggerMock(sandbox).mock, + }) - const getAllUsers = safeBind(getAllUsersCommandBuilder.getCommandFunction(), service) + const getAllUsers = safeBind(getAllUsersCommandBuilder.getCommandFunction(), service) - const payload: UserV1GetAllUsersInputPayload = undefined + const payload: UserV1GetAllUsersInputPayload = undefined - const parameter: UserV1GetAllUsersInputParameter = {} + const parameter: UserV1GetAllUsersInputParameter = {} - const context = getAllUsersCommandBuilder.getCommandContextMock(payload, parameter, sandbox) + const context = getAllUsersCommandBuilder.getCommandContextMock({ payload, parameter, sandbox }) - const userMock: User = { - email: 'email@example.com', - name: 'test user', - password: 'password', - userId: 'a5fef052-911c-472c-ac25-e2da327f0af5', - } + const userMock: User = { + email: 'email@example.com', + name: 'test user', + password: 'password', + userId: 'a5fef052-911c-472c-ac25-e2da327f0af5', + } - context.stubs.getState.resolves({ - [StateStoreKey.Users]: [userMock], - }) + context.stubs.getState.resolves({ + [StateStoreKey.Users]: [userMock], + }) - const result = await getAllUsers(context.mock, payload, parameter) + const result = await getAllUsers(context.mock, payload, parameter) - expect(result).toStrictEqual([{ name: userMock.name, email: userMock.email, userId: userMock.userId }]) - }) + expect(result).toStrictEqual([{ name: userMock.name, email: userMock.email, userId: userMock.userId }]) + }) }) diff --git a/examples/mqtt-bridge/src/service/user/v1/command/getAllUsers/getAllUsersCommandBuilder.ts b/examples/mqtt-bridge/src/service/user/v1/command/getAllUsers/getAllUsersCommandBuilder.ts index 482dda298..911cde6bf 100644 --- a/examples/mqtt-bridge/src/service/user/v1/command/getAllUsers/getAllUsersCommandBuilder.ts +++ b/examples/mqtt-bridge/src/service/user/v1/command/getAllUsers/getAllUsersCommandBuilder.ts @@ -2,20 +2,20 @@ import type { User } from '../../../../../types/index.js' import { StateStoreKey } from '../../../../../types/index.js' import { userV1ServiceBuilder } from '../../userV1ServiceBuilder.js' import { - userV1GetAllUsersInputParameterSchema, - userV1GetAllUsersInputPayloadSchema, - userV1GetAllUsersOutputPayloadSchema, + userV1GetAllUsersInputParameterSchema, + userV1GetAllUsersInputPayloadSchema, + userV1GetAllUsersOutputPayloadSchema, } from './schema.js' export const getAllUsersCommandBuilder = userV1ServiceBuilder - .getCommandBuilder('getAllUsers', 'returns a list of registered users') - .addPayloadSchema(userV1GetAllUsersInputPayloadSchema) - .addParameterSchema(userV1GetAllUsersInputParameterSchema) - .addOutputSchema(userV1GetAllUsersOutputPayloadSchema) - .exposeAsHttpEndpoint('GET', '/user') - .setCommandFunction(async function (context, _payload, _parameter) { - const result = (await context.states.getState(StateStoreKey.Users)) as { [StateStoreKey.Users]: User[] | undefined } - const users = result.users ?? [] + .getCommandBuilder('getAllUsers', 'returns a list of registered users') + .addPayloadSchema(userV1GetAllUsersInputPayloadSchema) + .addParameterSchema(userV1GetAllUsersInputParameterSchema) + .addOutputSchema(userV1GetAllUsersOutputPayloadSchema) + .exposeAsHttpEndpoint('GET', '/user') + .setCommandFunction(async function (context, _payload, _parameter) { + const result = (await context.states.getState(StateStoreKey.Users)) as { [StateStoreKey.Users]: User[] | undefined } + const users = result.users ?? [] - return users - }) + return users + }) diff --git a/examples/mqtt-bridge/src/service/user/v1/command/getAllUsers/schema.ts b/examples/mqtt-bridge/src/service/user/v1/command/getAllUsers/schema.ts index 32c736fc2..91a45623c 100644 --- a/examples/mqtt-bridge/src/service/user/v1/command/getAllUsers/schema.ts +++ b/examples/mqtt-bridge/src/service/user/v1/command/getAllUsers/schema.ts @@ -3,22 +3,22 @@ import { z } from 'zod' // define the input parameters export const userV1GetAllUsersInputParameterSchema = extendApi(z.object({}), { - title: 'get all users input parameter schema', + title: 'get all users input parameter schema', }) // define the input payload export const userV1GetAllUsersInputPayloadSchema = z.undefined() export const userV1GetAllUsersUserEntrySchema = z.object({ - userId: extendApi(z.string().uuid(), { title: 'the user id', example: 'a5fef052-911c-472c-ac25-e2da327f0af5' }), - email: extendApi(z.string().email(), { title: 'the email of the user to register', example: 'user@email.com' }), - name: extendApi(z.string().min(3), { - title: 'the name of the user to register', - example: 'User', - }), + userId: extendApi(z.string().uuid(), { title: 'the user id', example: 'a5fef052-911c-472c-ac25-e2da327f0af5' }), + email: extendApi(z.string().email(), { title: 'the email of the user to register', example: 'user@email.com' }), + name: extendApi(z.string().min(3), { + title: 'the name of the user to register', + example: 'User', + }), }) // define the output payload export const userV1GetAllUsersOutputPayloadSchema = extendApi(z.array(userV1GetAllUsersUserEntrySchema), { - title: 'get all users output payload schema', + title: 'get all users output payload schema', }) diff --git a/examples/mqtt-bridge/src/service/user/v1/command/getAllUsers/types.ts b/examples/mqtt-bridge/src/service/user/v1/command/getAllUsers/types.ts index c8486af0d..4e88b232e 100644 --- a/examples/mqtt-bridge/src/service/user/v1/command/getAllUsers/types.ts +++ b/examples/mqtt-bridge/src/service/user/v1/command/getAllUsers/types.ts @@ -1,9 +1,9 @@ import type { z } from 'zod' import type { - userV1GetAllUsersInputParameterSchema, - userV1GetAllUsersInputPayloadSchema, - userV1GetAllUsersOutputPayloadSchema, + userV1GetAllUsersInputParameterSchema, + userV1GetAllUsersInputPayloadSchema, + userV1GetAllUsersOutputPayloadSchema, } from './schema.js' export type UserV1GetAllUsersInputParameter = z.input diff --git a/examples/mqtt-bridge/src/service/user/v1/command/getUserById/getUserById.test.ts b/examples/mqtt-bridge/src/service/user/v1/command/getUserById/getUserById.test.ts index 74fe45729..b9bd14727 100644 --- a/examples/mqtt-bridge/src/service/user/v1/command/getUserById/getUserById.test.ts +++ b/examples/mqtt-bridge/src/service/user/v1/command/getUserById/getUserById.test.ts @@ -1,4 +1,4 @@ -import { getCommandContextMock, getEventBridgeMock, getLoggerMock, safeBind } from '@purista/core' +import { getEventBridgeMock, getLoggerMock, safeBind } from '@purista/core' import { createSandbox } from 'sinon' import type { User } from '../../../../../types/index.js' @@ -8,68 +8,68 @@ import { getUserByIdCommandBuilder } from './getUserByIdCommandBuilder.js' import type { UserV1GetUserByIdInputParameter, UserV1GetUserByIdInputPayload } from './types.js' describe('service User version 1 - command getUserById', () => { - let sandbox = createSandbox() - beforeEach(() => { - sandbox = createSandbox() - }) + let sandbox = createSandbox() + beforeEach(() => { + sandbox = createSandbox() + }) - afterEach(() => { - sandbox.restore() - }) + afterEach(() => { + sandbox.restore() + }) - test('returns a user', async () => { - const service = await userV1Service.getInstance(getEventBridgeMock(sandbox).mock, { - logger: getLoggerMock(sandbox).mock, - }) + test('returns a user', async () => { + const service = await userV1Service.getInstance(getEventBridgeMock(sandbox).mock, { + logger: getLoggerMock(sandbox).mock, + }) - const getUserById = safeBind(getUserByIdCommandBuilder.getCommandFunction(), service) + const getUserById = safeBind(getUserByIdCommandBuilder.getCommandFunction(), service) - const payload: UserV1GetUserByIdInputPayload = undefined + const payload: UserV1GetUserByIdInputPayload = undefined - const userMock: User = { - email: 'email@example.com', - name: 'test user', - password: 'password', - userId: 'a5fef052-911c-472c-ac25-e2da327f0af5', - } + const userMock: User = { + email: 'email@example.com', + name: 'test user', + password: 'password', + userId: 'a5fef052-911c-472c-ac25-e2da327f0af5', + } - const parameter: UserV1GetUserByIdInputParameter = { - userId: userMock.userId, - } + const parameter: UserV1GetUserByIdInputParameter = { + userId: userMock.userId, + } - const context = getUserByIdCommandBuilder.getCommandContextMock(payload, parameter, sandbox) + const context = getUserByIdCommandBuilder.getCommandContextMock({ payload, parameter, sandbox }) - context.stubs.getState.resolves({ [StateStoreKey.Users]: [userMock] }) + context.stubs.getState.resolves({ [StateStoreKey.Users]: [userMock] }) - const result = await getUserById(context.mock, payload, parameter) + const result = await getUserById(context.mock, payload, parameter) - expect(result).toStrictEqual({ email: userMock.email, name: userMock.name, userId: userMock.userId }) - }) + expect(result).toStrictEqual({ email: userMock.email, name: userMock.name, userId: userMock.userId }) + }) - test('throws if user can not be found', async () => { - const service = await userV1Service.getInstance(getEventBridgeMock(sandbox).mock, { - logger: getLoggerMock(sandbox).mock, - }) + test('throws if user can not be found', async () => { + const service = await userV1Service.getInstance(getEventBridgeMock(sandbox).mock, { + logger: getLoggerMock(sandbox).mock, + }) - const getUserById = safeBind(getUserByIdCommandBuilder.getCommandFunction(), service) + const getUserById = safeBind(getUserByIdCommandBuilder.getCommandFunction(), service) - const payload: UserV1GetUserByIdInputPayload = undefined + const payload: UserV1GetUserByIdInputPayload = undefined - const userMock: User = { - email: 'email@example.com', - name: 'test user', - password: 'password', - userId: 'a5fef052-911c-472c-ac25-e2da327f0af5', - } + const userMock: User = { + email: 'email@example.com', + name: 'test user', + password: 'password', + userId: 'a5fef052-911c-472c-ac25-e2da327f0af5', + } - const parameter: UserV1GetUserByIdInputParameter = { - userId: userMock.userId, - } + const parameter: UserV1GetUserByIdInputParameter = { + userId: userMock.userId, + } - const context = getCommandContextMock(payload, parameter, sandbox) + const context = getUserByIdCommandBuilder.getCommandContextMock({ payload, parameter, sandbox }) - context.stubs.getState.resolves({ [StateStoreKey.Users]: [] }) + context.stubs.getState.resolves({ [StateStoreKey.Users]: [] }) - await expect(getUserById(context.mock, payload, parameter)).rejects.toThrow('user could not be found') - }) + await expect(getUserById(context.mock, payload, parameter)).rejects.toThrow('user could not be found') + }) }) diff --git a/examples/mqtt-bridge/src/service/user/v1/command/getUserById/getUserByIdCommandBuilder.ts b/examples/mqtt-bridge/src/service/user/v1/command/getUserById/getUserByIdCommandBuilder.ts index 8d8e25d5f..5256e3242 100644 --- a/examples/mqtt-bridge/src/service/user/v1/command/getUserById/getUserByIdCommandBuilder.ts +++ b/examples/mqtt-bridge/src/service/user/v1/command/getUserById/getUserByIdCommandBuilder.ts @@ -4,26 +4,26 @@ import type { User } from '../../../../../types/index.js' import { StateStoreKey } from '../../../../../types/index.js' import { userV1ServiceBuilder } from '../../userV1ServiceBuilder.js' import { - userV1GetUserByIdInputParameterSchema, - userV1GetUserByIdInputPayloadSchema, - userV1GetUserByIdOutputPayloadSchema, + userV1GetUserByIdInputParameterSchema, + userV1GetUserByIdInputPayloadSchema, + userV1GetUserByIdOutputPayloadSchema, } from './schema.js' export const getUserByIdCommandBuilder = userV1ServiceBuilder - .getCommandBuilder('getUserById', 'returns the user given by the user id') - .addPayloadSchema(userV1GetUserByIdInputPayloadSchema) - .addParameterSchema(userV1GetUserByIdInputParameterSchema) - .addOutputSchema(userV1GetUserByIdOutputPayloadSchema) - .exposeAsHttpEndpoint('GET', 'user/:userId') - .setCommandFunction(async function (context, _payload, parameter) { - const result = (await context.states.getState(StateStoreKey.Users)) as { [StateStoreKey.Users]: User[] | undefined } - const users = result.users ?? [] + .getCommandBuilder('getUserById', 'returns the user given by the user id') + .addPayloadSchema(userV1GetUserByIdInputPayloadSchema) + .addParameterSchema(userV1GetUserByIdInputParameterSchema) + .addOutputSchema(userV1GetUserByIdOutputPayloadSchema) + .exposeAsHttpEndpoint('GET', 'user/:userId') + .setCommandFunction(async function (context, _payload, parameter) { + const result = (await context.states.getState(StateStoreKey.Users)) as { [StateStoreKey.Users]: User[] | undefined } + const users = result.users ?? [] - const user = users.find((user) => (user.userId = parameter.userId)) + const user = users.find(user => user.userId === parameter.userId) - if (!user) { - throw new HandledError(StatusCode.NotFound, 'user could not be found', { userId: parameter.userId }) - } + if (!user) { + throw new HandledError(StatusCode.NotFound, 'user could not be found', { userId: parameter.userId }) + } - return user - }) + return user + }) diff --git a/examples/mqtt-bridge/src/service/user/v1/command/getUserById/schema.ts b/examples/mqtt-bridge/src/service/user/v1/command/getUserById/schema.ts index 87b2549bb..06a7616ec 100644 --- a/examples/mqtt-bridge/src/service/user/v1/command/getUserById/schema.ts +++ b/examples/mqtt-bridge/src/service/user/v1/command/getUserById/schema.ts @@ -3,10 +3,10 @@ import { z } from 'zod' // define the input parameters export const userV1GetUserByIdInputParameterSchema = extendApi( - z.object({ - userId: extendApi(z.string().uuid(), { title: 'the user id', example: 'a5fef052-911c-472c-ac25-e2da327f0af5' }), - }), - { title: 'get user by id input payload schema' }, + z.object({ + userId: extendApi(z.string().uuid(), { title: 'the user id', example: 'a5fef052-911c-472c-ac25-e2da327f0af5' }), + }), + { title: 'get user by id input payload schema' }, ) // define the input payload @@ -14,13 +14,13 @@ export const userV1GetUserByIdInputPayloadSchema = z.undefined() // define the output payload export const userV1GetUserByIdOutputPayloadSchema = extendApi( - z.object({ - userId: extendApi(z.string().uuid(), { title: 'the user id', example: 'a5fef052-911c-472c-ac25-e2da327f0af5' }), - email: extendApi(z.string().email(), { title: 'the email of the user to register', example: 'user@email.com' }), - name: extendApi(z.string().min(3), { - title: 'the name of the user to register', - example: 'User', - }), - }), - { title: 'the sign up input' }, + z.object({ + userId: extendApi(z.string().uuid(), { title: 'the user id', example: 'a5fef052-911c-472c-ac25-e2da327f0af5' }), + email: extendApi(z.string().email(), { title: 'the email of the user to register', example: 'user@email.com' }), + name: extendApi(z.string().min(3), { + title: 'the name of the user to register', + example: 'User', + }), + }), + { title: 'the sign up input' }, ) diff --git a/examples/mqtt-bridge/src/service/user/v1/command/getUserById/types.ts b/examples/mqtt-bridge/src/service/user/v1/command/getUserById/types.ts index 7a3c6290c..59d70c337 100644 --- a/examples/mqtt-bridge/src/service/user/v1/command/getUserById/types.ts +++ b/examples/mqtt-bridge/src/service/user/v1/command/getUserById/types.ts @@ -1,9 +1,9 @@ import type { z } from 'zod' import type { - userV1GetUserByIdInputParameterSchema, - userV1GetUserByIdInputPayloadSchema, - userV1GetUserByIdOutputPayloadSchema, + userV1GetUserByIdInputParameterSchema, + userV1GetUserByIdInputPayloadSchema, + userV1GetUserByIdOutputPayloadSchema, } from './schema.js' export type UserV1GetUserByIdInputParameter = z.input diff --git a/examples/mqtt-bridge/src/service/user/v1/command/signUp/schema.ts b/examples/mqtt-bridge/src/service/user/v1/command/signUp/schema.ts index f969055f8..17d29b986 100644 --- a/examples/mqtt-bridge/src/service/user/v1/command/signUp/schema.ts +++ b/examples/mqtt-bridge/src/service/user/v1/command/signUp/schema.ts @@ -6,24 +6,24 @@ export const userV1SignUpInputParameterSchema = extendApi(z.object({}), { title: // define the input payload export const userV1SignUpInputPayloadSchema = extendApi( - z.object({ - email: extendApi(z.string().email(), { title: 'the email of the user to register', example: 'user@email.com' }), - name: extendApi(z.string().min(3), { - title: 'the name of the user to register', - example: 'User', - }), - password: extendApi(z.string().min(3), { - title: 'the name login password', - example: 'password', - }), - }), - { title: 'the sign up input' }, + z.object({ + email: extendApi(z.string().email(), { title: 'the email of the user to register', example: 'user@email.com' }), + name: extendApi(z.string().min(3), { + title: 'the name of the user to register', + example: 'User', + }), + password: extendApi(z.string().min(3), { + title: 'the name login password', + example: 'password', + }), + }), + { title: 'the sign up input' }, ) // define the output payload export const userV1SignUpOutputPayloadSchema = extendApi( - z.object({ - userId: extendApi(z.string().uuid(), { title: 'the user id', example: 'a5fef052-911c-472c-ac25-e2da327f0af5' }), - }), - { title: 'sign up output payload schema' }, + z.object({ + userId: extendApi(z.string().uuid(), { title: 'the user id', example: 'a5fef052-911c-472c-ac25-e2da327f0af5' }), + }), + { title: 'sign up output payload schema' }, ) diff --git a/examples/mqtt-bridge/src/service/user/v1/command/signUp/signUp.test.ts b/examples/mqtt-bridge/src/service/user/v1/command/signUp/signUp.test.ts index 5b4f31157..31bac83af 100644 --- a/examples/mqtt-bridge/src/service/user/v1/command/signUp/signUp.test.ts +++ b/examples/mqtt-bridge/src/service/user/v1/command/signUp/signUp.test.ts @@ -7,69 +7,69 @@ import { signUpCommandBuilder } from './signUpCommandBuilder.js' import type { UserV1SignUpInputParameter, UserV1SignUpInputPayload } from './types.js' describe('service User version 1 - command signUp', () => { - let sandbox = createSandbox() - beforeEach(() => { - sandbox = createSandbox() - }) + let sandbox = createSandbox() + beforeEach(() => { + sandbox = createSandbox() + }) - afterEach(() => { - sandbox.restore() - }) + afterEach(() => { + sandbox.restore() + }) - test('can register a new user', async () => { - const service = await userV1Service.getInstance(getEventBridgeMock(sandbox).mock, { - logger: getLoggerMock(sandbox).mock, - }) + test('can register a new user', async () => { + const service = await userV1Service.getInstance(getEventBridgeMock(sandbox).mock, { + logger: getLoggerMock(sandbox).mock, + }) - const signUp = safeBind(signUpCommandBuilder.getCommandFunction(), service) + const signUp = safeBind(signUpCommandBuilder.getCommandFunction(), service) - const payload: UserV1SignUpInputPayload = { - name: 'test user', - email: 'email@example.com', - password: 'password', - } + const payload: UserV1SignUpInputPayload = { + name: 'test user', + email: 'email@example.com', + password: 'password', + } - const parameter: UserV1SignUpInputParameter = {} + const parameter: UserV1SignUpInputParameter = {} - const context = signUpCommandBuilder.getCommandContextMock(payload, parameter, sandbox) + const context = signUpCommandBuilder.getCommandContextMock({ payload, parameter, sandbox }) - context.stubs.getState.resolves({}) - context.stubs.setState.resolves() + context.stubs.getState.resolves({}) + context.stubs.setState.resolves() - const result = await signUp(context.mock, payload, parameter) + const result = await signUp(context.mock, payload, parameter) - expect(result.userId).toBeDefined() - }) + expect(result.userId).toBeDefined() + }) - test('throws when a user with same email exist', async () => { - const service = await userV1Service.getInstance(getEventBridgeMock(sandbox).mock, { - logger: getLoggerMock(sandbox).mock, - }) + test('throws when a user with same email exist', async () => { + const service = await userV1Service.getInstance(getEventBridgeMock(sandbox).mock, { + logger: getLoggerMock(sandbox).mock, + }) - const signUp = safeBind(signUpCommandBuilder.getCommandFunction(), service) + const signUp = safeBind(signUpCommandBuilder.getCommandFunction(), service) - const payload: UserV1SignUpInputPayload = { - name: 'test user', - email: 'email@example.com', - password: 'password', - } + const payload: UserV1SignUpInputPayload = { + name: 'test user', + email: 'email@example.com', + password: 'password', + } - const parameter: UserV1SignUpInputParameter = {} + const parameter: UserV1SignUpInputParameter = {} - const context = signUpCommandBuilder.getCommandContextMock(payload, parameter, sandbox) + const context = signUpCommandBuilder.getCommandContextMock({ payload, parameter, sandbox }) - context.stubs.getState.resolves({ - [StateStoreKey.Users]: [ - { - name: 'test user', - email: 'email@example.com', - password: 'password', - userId: 'a5fef052-911c-472c-ac25-e2da327f0af5', - }, - ], - }) - context.stubs.setState.resolves() + context.stubs.getState.resolves({ + [StateStoreKey.Users]: [ + { + name: 'test user', + email: 'email@example.com', + password: 'password', + userId: 'a5fef052-911c-472c-ac25-e2da327f0af5', + }, + ], + }) + context.stubs.setState.resolves() - await expect(signUp(context.mock, payload, parameter)).rejects.toThrow('the user already exists') - }) + await expect(signUp(context.mock, payload, parameter)).rejects.toThrow('the user already exists') + }) }) diff --git a/examples/mqtt-bridge/src/service/user/v1/command/signUp/signUpCommandBuilder.ts b/examples/mqtt-bridge/src/service/user/v1/command/signUp/signUpCommandBuilder.ts index b44bbb80c..729c69670 100644 --- a/examples/mqtt-bridge/src/service/user/v1/command/signUp/signUpCommandBuilder.ts +++ b/examples/mqtt-bridge/src/service/user/v1/command/signUp/signUpCommandBuilder.ts @@ -7,37 +7,37 @@ import { StateStoreKey } from '../../../../../types/index.js' import { ServiceEvent } from '../../../../ServiceEvent.enum.js' import { userV1ServiceBuilder } from '../../userV1ServiceBuilder.js' import { - userV1SignUpInputParameterSchema, - userV1SignUpInputPayloadSchema, - userV1SignUpOutputPayloadSchema, + userV1SignUpInputParameterSchema, + userV1SignUpInputPayloadSchema, + userV1SignUpOutputPayloadSchema, } from './schema.js' export const signUpCommandBuilder = userV1ServiceBuilder - .getCommandBuilder('signUp', 'registers a new user at our product') - .setSuccessEventName(ServiceEvent.NewUserRegistered) - .addPayloadSchema(userV1SignUpInputPayloadSchema) - .addParameterSchema(userV1SignUpInputParameterSchema) - .addOutputSchema(userV1SignUpOutputPayloadSchema) - .exposeAsHttpEndpoint('POST', 'user/signup') - .setCommandFunction(async function (context, payload, _parameter) { - const result = (await context.states.getState(StateStoreKey.Users)) as { [StateStoreKey.Users]: User[] | undefined } - - if (result.users?.some((user) => user.email === payload.email)) { - throw new HandledError(StatusCode.BadRequest, 'the user already exists') - } - - const user: User = { - ...payload, - userId: randomUUID(), - } - - const users = result.users ?? [] - users.push(user) - - await context.states.setState(StateStoreKey.Users, users) - - // add your business logic here - context.logger.info('new user added') - - return user - }) + .getCommandBuilder('signUp', 'registers a new user at our product') + .setSuccessEventName(ServiceEvent.NewUserRegistered) + .addPayloadSchema(userV1SignUpInputPayloadSchema) + .addParameterSchema(userV1SignUpInputParameterSchema) + .addOutputSchema(userV1SignUpOutputPayloadSchema) + .exposeAsHttpEndpoint('POST', 'user/signup') + .setCommandFunction(async function (context, payload, _parameter) { + const result = (await context.states.getState(StateStoreKey.Users)) as { [StateStoreKey.Users]: User[] | undefined } + + if (result.users?.some(user => user.email === payload.email)) { + throw new HandledError(StatusCode.BadRequest, 'the user already exists') + } + + const user: User = { + ...payload, + userId: randomUUID(), + } + + const users = result.users ?? [] + users.push(user) + + await context.states.setState(StateStoreKey.Users, users) + + // add your business logic here + context.logger.info('new user added') + + return user + }) diff --git a/examples/mqtt-bridge/src/service/user/v1/command/signUp/types.ts b/examples/mqtt-bridge/src/service/user/v1/command/signUp/types.ts index 084667e07..a162fc0d0 100644 --- a/examples/mqtt-bridge/src/service/user/v1/command/signUp/types.ts +++ b/examples/mqtt-bridge/src/service/user/v1/command/signUp/types.ts @@ -1,9 +1,9 @@ import type { z } from 'zod' import type { - userV1SignUpInputParameterSchema, - userV1SignUpInputPayloadSchema, - userV1SignUpOutputPayloadSchema, + userV1SignUpInputParameterSchema, + userV1SignUpInputPayloadSchema, + userV1SignUpOutputPayloadSchema, } from './schema.js' export type UserV1SignUpInputParameter = z.input diff --git a/examples/mqtt-bridge/src/service/user/v1/userV1Service.test.ts b/examples/mqtt-bridge/src/service/user/v1/userV1Service.test.ts index 47603a4b9..7efe0970c 100644 --- a/examples/mqtt-bridge/src/service/user/v1/userV1Service.test.ts +++ b/examples/mqtt-bridge/src/service/user/v1/userV1Service.test.ts @@ -1,11 +1,7 @@ import { userV1Service as service } from './userV1Service.js' describe('service user version 1', () => { - it('has valid commands', () => { - service.validateCommandDefinitions() - }) - - it('has valid subscriptions', () => { - service.validateSubscriptionDefinitions() - }) + it('has valid configuration', () => { + service.testServiceSetup() + }) }) diff --git a/examples/mqtt-bridge/src/service/user/v1/userV1Service.ts b/examples/mqtt-bridge/src/service/user/v1/userV1Service.ts index b483dcc51..2d972c27b 100644 --- a/examples/mqtt-bridge/src/service/user/v1/userV1Service.ts +++ b/examples/mqtt-bridge/src/service/user/v1/userV1Service.ts @@ -10,13 +10,13 @@ import { userV1ServiceBuilder } from './userV1ServiceBuilder.js' // other service config should be done in ./userServiceBuilder.ts file const commandDefinitions: CommandDefinitionList = [ - signUpCommandBuilder.getDefinition(), - getUserByIdCommandBuilder.getDefinition(), - getAllUsersCommandBuilder.getDefinition(), + signUpCommandBuilder.getDefinition(), + getUserByIdCommandBuilder.getDefinition(), + getAllUsersCommandBuilder.getDefinition(), ] const subscriptionDefinitions: SubscriptionDefinitionList = [] export const userV1Service = userV1ServiceBuilder - .addCommandDefinition(...commandDefinitions) - .addSubscriptionDefinition(...subscriptionDefinitions) + .addCommandDefinition(...commandDefinitions) + .addSubscriptionDefinition(...subscriptionDefinitions) diff --git a/examples/mqtt-bridge/src/service/user/v1/userV1ServiceBuilder.ts b/examples/mqtt-bridge/src/service/user/v1/userV1ServiceBuilder.ts index b047cf552..57a76d32d 100644 --- a/examples/mqtt-bridge/src/service/user/v1/userV1ServiceBuilder.ts +++ b/examples/mqtt-bridge/src/service/user/v1/userV1ServiceBuilder.ts @@ -5,12 +5,10 @@ import { generalUserServiceInfo } from '../generalUserServiceInfo.js' import { userServiceV1ConfigSchema } from './userServiceConfig.js' export const userServiceInfo = { - serviceVersion: '1', - ...generalUserServiceInfo, + serviceVersion: '1', + ...generalUserServiceInfo, } as const satisfies ServiceInfoType // create a service builder instance and assign service config schema and default config. -export const userV1ServiceBuilder = new ServiceBuilder(userServiceInfo) - .setConfigSchema(userServiceV1ConfigSchema) - .setDefaultConfig({}) +export const userV1ServiceBuilder = new ServiceBuilder(userServiceInfo).setConfigSchema(userServiceV1ConfigSchema) diff --git a/examples/mqtt-bridge/src/types/StateStoreKey.enum.ts b/examples/mqtt-bridge/src/types/StateStoreKey.enum.ts index a25d9271c..7992eeae1 100644 --- a/examples/mqtt-bridge/src/types/StateStoreKey.enum.ts +++ b/examples/mqtt-bridge/src/types/StateStoreKey.enum.ts @@ -1,3 +1,3 @@ export enum StateStoreKey { - Users = 'users', + Users = 'users', } diff --git a/examples/mqtt-bridge/src/types/User.ts b/examples/mqtt-bridge/src/types/User.ts index 8e2616607..de6239fbf 100644 --- a/examples/mqtt-bridge/src/types/User.ts +++ b/examples/mqtt-bridge/src/types/User.ts @@ -1,6 +1,6 @@ export type User = { - email: string - name: string - password: string - userId: string + email: string + name: string + password: string + userId: string } diff --git a/examples/mqtt-bridge/tsconfig.json b/examples/mqtt-bridge/tsconfig.json index a84ca294e..7d9abe7a2 100644 --- a/examples/mqtt-bridge/tsconfig.json +++ b/examples/mqtt-bridge/tsconfig.json @@ -1,11 +1,8 @@ { - "extends": "../../tsconfig.json", - - "compilerOptions": { - "outDir": "./build" - }, - "files": [ - "src/index.ts", - "fastify.d.ts" - ], -} \ No newline at end of file + "extends": "../../tsconfig.json", + + "compilerOptions": { + "outDir": "./build" + }, + "files": ["src/index.ts", "fastify.d.ts"] +} diff --git a/examples/nats-bridge/package.json b/examples/nats-bridge/package.json index 90a14b784..b7c6fa2ba 100644 --- a/examples/nats-bridge/package.json +++ b/examples/nats-bridge/package.json @@ -1,41 +1,41 @@ { - "name": "@purista/nats-example", - "version": "1.11.0", - "description": "purista backend framework", - "homepage": "https://purista.dev", - "private": true, - "repository": { - "type": "git", - "url": "git@github.com:sebastianwessel/purista.git" - }, - "author": "Sebastian Wessel", - "license": "ISC", - "type": "module", - "main": "src/index.ts", - "engines": { - "node": ">=18.15" - }, - "scripts": { - "start": "tsx src/index.ts | pino-pretty", - "nats:up": "docker start purista-nats || docker run -p 4222:4222 --name purista-nats nats:alpine -js", - "nats:down": "docker container stop $(docker container ls -q --filter name=purista-nats)", - "lint": "eslint . --ext .ts,.json --cache . --fix", - "test": "vitest" - }, - "devDependencies": { - "@types/node": "^20.11.17", - "pino-pretty": "^10.3.1", - "sinon": "^17.0.1", - "tsx": "^4.7.0", - "typescript": "^5.3.3", - "vitest": "^1.3.0" - }, - "dependencies": { - "@fastify/static": "^7.0.1", - "@purista/core": "*", - "@purista/httpserver": "*", - "@purista/nats-state-store": "*", - "@purista/natsbridge": "*", - "zod": "^3.22.4" - } + "name": "@purista/nats-example", + "version": "1.11.0", + "description": "purista backend framework", + "homepage": "https://purista.dev", + "private": true, + "repository": { + "type": "git", + "url": "git@github.com:puristajs/purista.git" + }, + "author": "Sebastian Wessel", + "license": "ISC", + "type": "module", + "main": "src/index.ts", + "engines": { + "node": ">=18.15" + }, + "scripts": { + "start": "tsx src/index.ts | pino-pretty", + "nats:up": "docker start purista-nats || docker run -p 4222:4222 --name purista-nats nats:alpine -js", + "nats:down": "docker container stop $(docker container ls -q --filter name=purista-nats)", + "lint": "npx @biomejs/biome check --write", + "test": "vitest" + }, + "devDependencies": { + "@types/node": "^22.5.1", + "pino-pretty": "^13.0.0", + "sinon": "^19.0.2", + "tsx": "^4.19.0", + "typescript": "^5.5.4", + "vitest": "^3.0.4" + }, + "dependencies": { + "@fastify/static": "^8.0.3", + "@purista/core": "*", + "@purista/httpserver": "*", + "@purista/nats-state-store": "*", + "@purista/natsbridge": "*", + "zod": "^3.24.1" + } } diff --git a/examples/nats-bridge/src/config/httpServerConfig.ts b/examples/nats-bridge/src/config/httpServerConfig.ts index 4ad70a945..89808c616 100644 --- a/examples/nats-bridge/src/config/httpServerConfig.ts +++ b/examples/nats-bridge/src/config/httpServerConfig.ts @@ -1,20 +1,20 @@ import type { HttpServerServiceV1Config } from '@purista/httpserver' const httpServerConfig: HttpServerServiceV1Config = { - fastify: {}, - port: 8080, - logLevel: 'debug', - domain: 'localhost', - apiMountPath: '/api', - enableCors: false, - openApi: { - enabled: true, - info: { - title: 'backend api', - description: 'OpenApi definition for server endpoints', - version: '1.0.0', - }, - }, + fastify: {}, + port: 8080, + logLevel: 'debug', + domain: 'localhost', + apiMountPath: '/api', + enableCors: false, + openApi: { + enabled: true, + info: { + title: 'backend api', + description: 'OpenApi definition for server endpoints', + version: '1.0.0', + }, + }, } export default httpServerConfig diff --git a/examples/nats-bridge/src/main.ts b/examples/nats-bridge/src/main.ts index 3c0d56007..1741586bd 100644 --- a/examples/nats-bridge/src/main.ts +++ b/examples/nats-bridge/src/main.ts @@ -11,49 +11,49 @@ import { emailV1Service } from './service/email/v1/index.js' import { userV1Service } from './service/user/v1/index.js' export const main = async () => { - // initialize the logging - const logger = initLogger('debug') + // initialize the logging + const logger = initLogger('debug') - logger.info('application starts') + logger.info('application starts') - // create and init our eventbridge - const eventBridge = new NatsBridge({ logger }) - await eventBridge.start() + // create and init our eventbridge + const eventBridge = new NatsBridge({ logger }) + await eventBridge.start() - // create a state store - const stateStore = new NatsStateStore({ logger }) + // create a state store + const stateStore = new NatsStateStore({ logger }) - // create and init a webserver - const httpServerService = await httpServerV1Service.getInstance(eventBridge, { - serviceConfig: httpServerConfig, - }) + // create and init a webserver + const httpServerService = await httpServerV1Service.getInstance(eventBridge, { + serviceConfig: httpServerConfig, + }) - const defaultPublicPath = resolve(import.meta.url, '..', 'public') + const defaultPublicPath = resolve(import.meta.url, '..', 'public') - // static file handler - httpServerService.server?.register(fastifyStatic, { - root: defaultPublicPath, - decorateReply: false, - }) + // static file handler + httpServerService.server?.register(fastifyStatic, { + root: defaultPublicPath, + decorateReply: false, + }) - // start the webserver - await httpServerService.start() + // start the webserver + await httpServerService.start() - const userService = await userV1Service.getInstance(eventBridge, { logger, stateStore }) - await userService.start() + const userService = await userV1Service.getInstance(eventBridge, { logger, stateStore }) + await userService.start() - const emailService = await emailV1Service.getInstance(eventBridge, { logger, stateStore }) - await emailService.start() + const emailService = await emailV1Service.getInstance(eventBridge, { logger, stateStore }) + await emailService.start() - logger.info('application ready') - logger.info(`open in browser: http://localhost:${httpServerConfig.port}`) + logger.info('application ready') + logger.info(`open in browser: http://localhost:${httpServerConfig.port}`) - gracefulShutdown(logger, [ - // begin with the event bridge to no longer accept incoming messages - eventBridge, - userService, - emailService, - stateStore, - httpServerService, - ]) + gracefulShutdown(logger, [ + // begin with the event bridge to no longer accept incoming messages + eventBridge, + userService, + emailService, + stateStore, + httpServerService, + ]) } diff --git a/examples/nats-bridge/src/service/ServiceEvent.enum.ts b/examples/nats-bridge/src/service/ServiceEvent.enum.ts index 5673fb208..a9103a39b 100644 --- a/examples/nats-bridge/src/service/ServiceEvent.enum.ts +++ b/examples/nats-bridge/src/service/ServiceEvent.enum.ts @@ -1,12 +1,12 @@ export enum ServiceEvent { - /** - * Emitted by user v1 command signUp: - * a new user registration - */ - NewUserRegistered = 'newUserRegistered', - /** - * Emitted by email v1 subscription sendWelcomeEmail: - * a new user registration - */ - WelcomeEmailSent = 'send a welcome mail to new registered users', + /** + * Emitted by user v1 command signUp: + * a new user registration + */ + NewUserRegistered = 'newUserRegistered', + /** + * Emitted by email v1 subscription sendWelcomeEmail: + * a new user registration + */ + WelcomeEmailSent = 'send a welcome mail to new registered users', } diff --git a/examples/nats-bridge/src/service/email/generalEmailServiceInfo.ts b/examples/nats-bridge/src/service/email/generalEmailServiceInfo.ts index c8cbc5538..446d54e53 100644 --- a/examples/nats-bridge/src/service/email/generalEmailServiceInfo.ts +++ b/examples/nats-bridge/src/service/email/generalEmailServiceInfo.ts @@ -1,6 +1,6 @@ import type { ServiceInfoType } from '@purista/core' export const generalEmailServiceInfo = { - serviceName: 'Email', - serviceDescription: 'sends emails to customers', + serviceName: 'Email', + serviceDescription: 'sends emails to customers', } as const satisfies Omit diff --git a/examples/nats-bridge/src/service/email/v1/emailV1Service.test.ts b/examples/nats-bridge/src/service/email/v1/emailV1Service.test.ts index 5b9b12e07..7729e529c 100644 --- a/examples/nats-bridge/src/service/email/v1/emailV1Service.test.ts +++ b/examples/nats-bridge/src/service/email/v1/emailV1Service.test.ts @@ -1,11 +1,7 @@ import { emailV1Service as service } from './emailV1Service.js' describe('service email version 1', () => { - it('has valid commands', () => { - service.validateCommandDefinitions() - }) - - it('has valid subscriptions', () => { - service.validateSubscriptionDefinitions() - }) + it('has valid configuration', () => { + service.testServiceSetup() + }) }) diff --git a/examples/nats-bridge/src/service/email/v1/emailV1Service.ts b/examples/nats-bridge/src/service/email/v1/emailV1Service.ts index 6b8cdf599..8535f9ba3 100644 --- a/examples/nats-bridge/src/service/email/v1/emailV1Service.ts +++ b/examples/nats-bridge/src/service/email/v1/emailV1Service.ts @@ -12,5 +12,5 @@ const commandDefinitions: CommandDefinitionList = [] const subscriptionDefinitions: SubscriptionDefinitionList = [sendWelcomeEmailSubscriptionBuilder.getDefinition()] export const emailV1Service = emailV1ServiceBuilder - .addCommandDefinition(...commandDefinitions) - .addSubscriptionDefinition(...subscriptionDefinitions) + .addCommandDefinition(...commandDefinitions) + .addSubscriptionDefinition(...subscriptionDefinitions) diff --git a/examples/nats-bridge/src/service/email/v1/emailV1ServiceBuilder.ts b/examples/nats-bridge/src/service/email/v1/emailV1ServiceBuilder.ts index ab2fd3b9a..926b0389e 100644 --- a/examples/nats-bridge/src/service/email/v1/emailV1ServiceBuilder.ts +++ b/examples/nats-bridge/src/service/email/v1/emailV1ServiceBuilder.ts @@ -5,12 +5,10 @@ import { generalEmailServiceInfo } from '../generalEmailServiceInfo.js' import { emailServiceV1ConfigSchema } from './emailServiceConfig.js' export const emailServiceInfo = { - serviceVersion: '1', - ...generalEmailServiceInfo, + serviceVersion: '1', + ...generalEmailServiceInfo, } as const satisfies ServiceInfoType // create a service builder instance and assign service config schema and default config. -export const emailV1ServiceBuilder = new ServiceBuilder(emailServiceInfo) - .setConfigSchema(emailServiceV1ConfigSchema) - .setDefaultConfig({}) +export const emailV1ServiceBuilder = new ServiceBuilder(emailServiceInfo).setConfigSchema(emailServiceV1ConfigSchema) diff --git a/examples/nats-bridge/src/service/email/v1/subscription/sendWelcomeEmail/schema.ts b/examples/nats-bridge/src/service/email/v1/subscription/sendWelcomeEmail/schema.ts index 97bd10475..227f140eb 100644 --- a/examples/nats-bridge/src/service/email/v1/subscription/sendWelcomeEmail/schema.ts +++ b/examples/nats-bridge/src/service/email/v1/subscription/sendWelcomeEmail/schema.ts @@ -2,9 +2,9 @@ import { z } from 'zod' // define the input payload export const emailV1SendWelcomeEmailInputPayloadSchema = z.object({ - userId: z.string().uuid(), + userId: z.string().uuid(), }) export const emailV1SendWelcomeEmailOutputPayloadSchema = z.object({ - userId: z.string().uuid(), + userId: z.string().uuid(), }) diff --git a/examples/nats-bridge/src/service/email/v1/subscription/sendWelcomeEmail/sendWelcomeEmail.test.ts b/examples/nats-bridge/src/service/email/v1/subscription/sendWelcomeEmail/sendWelcomeEmail.test.ts index 1e0f8fb27..20ad177a5 100644 --- a/examples/nats-bridge/src/service/email/v1/subscription/sendWelcomeEmail/sendWelcomeEmail.test.ts +++ b/examples/nats-bridge/src/service/email/v1/subscription/sendWelcomeEmail/sendWelcomeEmail.test.ts @@ -7,54 +7,54 @@ import { sendWelcomeEmailSubscriptionBuilder } from './sendWelcomeEmailSubscript import type { EmailV1SendWelcomeEmailInputPayload } from './types.js' describe('service Email version 1 - subscription sendWelcomeEmail', () => { - let sandbox = createSandbox() - beforeEach(() => { - sandbox = createSandbox() - }) - - afterEach(() => { - sandbox.restore() - }) - - test('sends an email', async () => { - // create a service instance to be bind to the subscription function - const service = await emailV1Service.getInstance(getEventBridgeMock(sandbox).mock, { - logger: getLoggerMock(sandbox).mock, - }) - - // get the subscription function and bind to service instance to work properly - const sendWelcomeEmail = safeBind(sendWelcomeEmailSubscriptionBuilder.getSubscriptionFunction(), service) - - const userMock: User = { - email: 'email@example.com', - name: 'test user', - password: 'password', - userId: 'a5fef052-911c-472c-ac25-e2da327f0af5', - } - - // define the test input payload - const payload: EmailV1SendWelcomeEmailInputPayload = { userId: userMock.userId } - - // define the test input parameter - const parameter = undefined as unknown as Readonly - - // create a mock message with the expected input for the subscription function - const message = getCommandSuccessMessageMock(payload) - - // create a subscription context for the subscription function - const context = sendWelcomeEmailSubscriptionBuilder.getSubscriptionContextMock(message, sandbox) - - context.stubs.service.User['1'].getUserById.resolves(userMock) - context.stubs.getConfig.resolves({ emailProviderUrl: 'https://example.com' }) - context.stubs.getSecret.resolves({ emailProviderAuthToken: 'secret_token' }) - - // execute the subscription function - const result = await sendWelcomeEmail(context.mock, payload, parameter) - - expect( - context.stubs.logger.debug.calledWith('Using email provider https://example.com with token secret_token'), - ).toBeTruthy() - expect(context.stubs.logger.info.calledWith('Welcome email to user sent to ' + userMock.email)).toBeTruthy() - expect(result).toStrictEqual({ userId: userMock.userId }) - }) + let sandbox = createSandbox() + beforeEach(() => { + sandbox = createSandbox() + }) + + afterEach(() => { + sandbox.restore() + }) + + test('sends an email', async () => { + // create a service instance to be bind to the subscription function + const service = await emailV1Service.getInstance(getEventBridgeMock(sandbox).mock, { + logger: getLoggerMock(sandbox).mock, + }) + + // get the subscription function and bind to service instance to work properly + const sendWelcomeEmail = safeBind(sendWelcomeEmailSubscriptionBuilder.getSubscriptionFunction(), service) + + const userMock: User = { + email: 'email@example.com', + name: 'test user', + password: 'password', + userId: 'a5fef052-911c-472c-ac25-e2da327f0af5', + } + + // define the test input payload + const payload: EmailV1SendWelcomeEmailInputPayload = { userId: userMock.userId } + + // define the test input parameter + const parameter = undefined as unknown as Readonly + + // create a mock message with the expected input for the subscription function + const message = getCommandSuccessMessageMock(payload) + + // create a subscription context for the subscription function + const context = sendWelcomeEmailSubscriptionBuilder.getSubscriptionContextMock({ message, sandbox }) + + context.stubs.service.User['1'].getUserById.resolves(userMock) + context.stubs.getConfig.resolves({ emailProviderUrl: 'https://example.com' }) + context.stubs.getSecret.resolves({ emailProviderAuthToken: 'secret_token' }) + + // execute the subscription function + const result = await sendWelcomeEmail(context.mock, payload, parameter) + + expect( + context.stubs.logger.debug.calledWith('Using email provider https://example.com with token secret_token'), + ).toBeTruthy() + expect(context.stubs.logger.info.calledWith(`Welcome email to user sent to ${userMock.email}`)).toBeTruthy() + expect(result).toStrictEqual({ userId: userMock.userId }) + }) }) diff --git a/examples/nats-bridge/src/service/email/v1/subscription/sendWelcomeEmail/sendWelcomeEmailSubscriptionBuilder.ts b/examples/nats-bridge/src/service/email/v1/subscription/sendWelcomeEmail/sendWelcomeEmailSubscriptionBuilder.ts index 59001104e..58c99facc 100644 --- a/examples/nats-bridge/src/service/email/v1/subscription/sendWelcomeEmail/sendWelcomeEmailSubscriptionBuilder.ts +++ b/examples/nats-bridge/src/service/email/v1/subscription/sendWelcomeEmail/sendWelcomeEmailSubscriptionBuilder.ts @@ -1,35 +1,35 @@ import { ServiceEvent } from '../../../../ServiceEvent.enum.js' import { - userV1GetUserByIdInputParameterSchema, - userV1GetUserByIdInputPayloadSchema, - userV1GetUserByIdOutputPayloadSchema, + userV1GetUserByIdInputParameterSchema, + userV1GetUserByIdInputPayloadSchema, + userV1GetUserByIdOutputPayloadSchema, } from '../../../../user/v1/command/getUserById/schema.js' import { emailV1ServiceBuilder } from '../../emailV1ServiceBuilder.js' import { emailV1SendWelcomeEmailInputPayloadSchema, emailV1SendWelcomeEmailOutputPayloadSchema } from './schema.js' export const sendWelcomeEmailSubscriptionBuilder = emailV1ServiceBuilder - .getSubscriptionBuilder('sendWelcomeEmail', 'send a welcome mail to new registered users') - .subscribeToEvent(ServiceEvent.NewUserRegistered) - .addPayloadSchema(emailV1SendWelcomeEmailInputPayloadSchema) - .addOutputSchema(ServiceEvent.WelcomeEmailSent, emailV1SendWelcomeEmailOutputPayloadSchema) - .canInvoke( - 'User', - '1', - 'getUserById', - userV1GetUserByIdOutputPayloadSchema, - userV1GetUserByIdInputPayloadSchema, - userV1GetUserByIdInputParameterSchema, - ) - .setSubscriptionFunction(async function (context, payload, _parameter) { - const config = await context.configs.getConfig('emailProviderUrl') - const secrets = await context.secrets.getSecret('emailProviderAuthToken') + .getSubscriptionBuilder('sendWelcomeEmail', 'send a welcome mail to new registered users') + .subscribeToEvent(ServiceEvent.NewUserRegistered) + .addPayloadSchema(emailV1SendWelcomeEmailInputPayloadSchema) + .addOutputSchema(ServiceEvent.WelcomeEmailSent, emailV1SendWelcomeEmailOutputPayloadSchema) + .canInvoke( + 'User', + '1', + 'getUserById', + userV1GetUserByIdOutputPayloadSchema, + userV1GetUserByIdInputPayloadSchema, + userV1GetUserByIdInputParameterSchema, + ) + .setSubscriptionFunction(async function (context, payload, _parameter) { + const config = await context.configs.getConfig('emailProviderUrl') + const secrets = await context.secrets.getSecret('emailProviderAuthToken') - context.logger.debug(`Using email provider ${config.emailProviderUrl} with token ${secrets.emailProviderAuthToken}`) + context.logger.debug(`Using email provider ${config.emailProviderUrl} with token ${secrets.emailProviderAuthToken}`) - const user = await context.service.User['1'].getUserById(undefined, payload) + const user = await context.service.User['1'].getUserById(undefined, payload) - // add your business logic here - context.logger.info(`Welcome email to user sent to ${user.email}`) + // add your business logic here + context.logger.info(`Welcome email to user sent to ${user.email}`) - return payload - }) + return payload + }) diff --git a/examples/nats-bridge/src/service/user/generalUserServiceInfo.ts b/examples/nats-bridge/src/service/user/generalUserServiceInfo.ts index b5e2bafd7..770f5c84e 100644 --- a/examples/nats-bridge/src/service/user/generalUserServiceInfo.ts +++ b/examples/nats-bridge/src/service/user/generalUserServiceInfo.ts @@ -1,6 +1,6 @@ import type { ServiceInfoType } from '@purista/core' export const generalUserServiceInfo = { - serviceName: 'User', - serviceDescription: 'manage user information', + serviceName: 'User', + serviceDescription: 'manage user information', } as const satisfies Omit diff --git a/examples/nats-bridge/src/service/user/v1/command/getAllUsers/getAllUsers.test.ts b/examples/nats-bridge/src/service/user/v1/command/getAllUsers/getAllUsers.test.ts index 3917b01dd..2e1b8032e 100644 --- a/examples/nats-bridge/src/service/user/v1/command/getAllUsers/getAllUsers.test.ts +++ b/examples/nats-bridge/src/service/user/v1/command/getAllUsers/getAllUsers.test.ts @@ -8,41 +8,41 @@ import { getAllUsersCommandBuilder } from './getAllUsersCommandBuilder.js' import type { UserV1GetAllUsersInputParameter, UserV1GetAllUsersInputPayload } from './types.js' describe('service User version 1 - command getAllUsers', () => { - let sandbox = createSandbox() - beforeEach(() => { - sandbox = createSandbox() - }) + let sandbox = createSandbox() + beforeEach(() => { + sandbox = createSandbox() + }) - afterEach(() => { - sandbox.restore() - }) + afterEach(() => { + sandbox.restore() + }) - test('does not throw', async () => { - const service = await userV1Service.getInstance(getEventBridgeMock(sandbox).mock, { - logger: getLoggerMock(sandbox).mock, - }) + test('does not throw', async () => { + const service = await userV1Service.getInstance(getEventBridgeMock(sandbox).mock, { + logger: getLoggerMock(sandbox).mock, + }) - const getAllUsers = safeBind(getAllUsersCommandBuilder.getCommandFunction(), service) + const getAllUsers = safeBind(getAllUsersCommandBuilder.getCommandFunction(), service) - const payload: UserV1GetAllUsersInputPayload = undefined + const payload: UserV1GetAllUsersInputPayload = undefined - const parameter: UserV1GetAllUsersInputParameter = {} + const parameter: UserV1GetAllUsersInputParameter = {} - const context = getAllUsersCommandBuilder.getCommandContextMock(payload, parameter, sandbox) + const context = getAllUsersCommandBuilder.getCommandContextMock({ payload, parameter, sandbox }) - const userMock: User = { - email: 'email@example.com', - name: 'test user', - password: 'password', - userId: 'a5fef052-911c-472c-ac25-e2da327f0af5', - } + const userMock: User = { + email: 'email@example.com', + name: 'test user', + password: 'password', + userId: 'a5fef052-911c-472c-ac25-e2da327f0af5', + } - context.stubs.getState.resolves({ - [StateStoreKey.Users]: [userMock], - }) + context.stubs.getState.resolves({ + [StateStoreKey.Users]: [userMock], + }) - const result = await getAllUsers(context.mock, payload, parameter) + const result = await getAllUsers(context.mock, payload, parameter) - expect(result).toStrictEqual([{ name: userMock.name, email: userMock.email, userId: userMock.userId }]) - }) + expect(result).toStrictEqual([{ name: userMock.name, email: userMock.email, userId: userMock.userId }]) + }) }) diff --git a/examples/nats-bridge/src/service/user/v1/command/getAllUsers/getAllUsersCommandBuilder.ts b/examples/nats-bridge/src/service/user/v1/command/getAllUsers/getAllUsersCommandBuilder.ts index 482dda298..911cde6bf 100644 --- a/examples/nats-bridge/src/service/user/v1/command/getAllUsers/getAllUsersCommandBuilder.ts +++ b/examples/nats-bridge/src/service/user/v1/command/getAllUsers/getAllUsersCommandBuilder.ts @@ -2,20 +2,20 @@ import type { User } from '../../../../../types/index.js' import { StateStoreKey } from '../../../../../types/index.js' import { userV1ServiceBuilder } from '../../userV1ServiceBuilder.js' import { - userV1GetAllUsersInputParameterSchema, - userV1GetAllUsersInputPayloadSchema, - userV1GetAllUsersOutputPayloadSchema, + userV1GetAllUsersInputParameterSchema, + userV1GetAllUsersInputPayloadSchema, + userV1GetAllUsersOutputPayloadSchema, } from './schema.js' export const getAllUsersCommandBuilder = userV1ServiceBuilder - .getCommandBuilder('getAllUsers', 'returns a list of registered users') - .addPayloadSchema(userV1GetAllUsersInputPayloadSchema) - .addParameterSchema(userV1GetAllUsersInputParameterSchema) - .addOutputSchema(userV1GetAllUsersOutputPayloadSchema) - .exposeAsHttpEndpoint('GET', '/user') - .setCommandFunction(async function (context, _payload, _parameter) { - const result = (await context.states.getState(StateStoreKey.Users)) as { [StateStoreKey.Users]: User[] | undefined } - const users = result.users ?? [] + .getCommandBuilder('getAllUsers', 'returns a list of registered users') + .addPayloadSchema(userV1GetAllUsersInputPayloadSchema) + .addParameterSchema(userV1GetAllUsersInputParameterSchema) + .addOutputSchema(userV1GetAllUsersOutputPayloadSchema) + .exposeAsHttpEndpoint('GET', '/user') + .setCommandFunction(async function (context, _payload, _parameter) { + const result = (await context.states.getState(StateStoreKey.Users)) as { [StateStoreKey.Users]: User[] | undefined } + const users = result.users ?? [] - return users - }) + return users + }) diff --git a/examples/nats-bridge/src/service/user/v1/command/getAllUsers/schema.ts b/examples/nats-bridge/src/service/user/v1/command/getAllUsers/schema.ts index 32c736fc2..91a45623c 100644 --- a/examples/nats-bridge/src/service/user/v1/command/getAllUsers/schema.ts +++ b/examples/nats-bridge/src/service/user/v1/command/getAllUsers/schema.ts @@ -3,22 +3,22 @@ import { z } from 'zod' // define the input parameters export const userV1GetAllUsersInputParameterSchema = extendApi(z.object({}), { - title: 'get all users input parameter schema', + title: 'get all users input parameter schema', }) // define the input payload export const userV1GetAllUsersInputPayloadSchema = z.undefined() export const userV1GetAllUsersUserEntrySchema = z.object({ - userId: extendApi(z.string().uuid(), { title: 'the user id', example: 'a5fef052-911c-472c-ac25-e2da327f0af5' }), - email: extendApi(z.string().email(), { title: 'the email of the user to register', example: 'user@email.com' }), - name: extendApi(z.string().min(3), { - title: 'the name of the user to register', - example: 'User', - }), + userId: extendApi(z.string().uuid(), { title: 'the user id', example: 'a5fef052-911c-472c-ac25-e2da327f0af5' }), + email: extendApi(z.string().email(), { title: 'the email of the user to register', example: 'user@email.com' }), + name: extendApi(z.string().min(3), { + title: 'the name of the user to register', + example: 'User', + }), }) // define the output payload export const userV1GetAllUsersOutputPayloadSchema = extendApi(z.array(userV1GetAllUsersUserEntrySchema), { - title: 'get all users output payload schema', + title: 'get all users output payload schema', }) diff --git a/examples/nats-bridge/src/service/user/v1/command/getAllUsers/types.ts b/examples/nats-bridge/src/service/user/v1/command/getAllUsers/types.ts index c8486af0d..4e88b232e 100644 --- a/examples/nats-bridge/src/service/user/v1/command/getAllUsers/types.ts +++ b/examples/nats-bridge/src/service/user/v1/command/getAllUsers/types.ts @@ -1,9 +1,9 @@ import type { z } from 'zod' import type { - userV1GetAllUsersInputParameterSchema, - userV1GetAllUsersInputPayloadSchema, - userV1GetAllUsersOutputPayloadSchema, + userV1GetAllUsersInputParameterSchema, + userV1GetAllUsersInputPayloadSchema, + userV1GetAllUsersOutputPayloadSchema, } from './schema.js' export type UserV1GetAllUsersInputParameter = z.input diff --git a/examples/nats-bridge/src/service/user/v1/command/getUserById/getUserById.test.ts b/examples/nats-bridge/src/service/user/v1/command/getUserById/getUserById.test.ts index ef9664911..b9bd14727 100644 --- a/examples/nats-bridge/src/service/user/v1/command/getUserById/getUserById.test.ts +++ b/examples/nats-bridge/src/service/user/v1/command/getUserById/getUserById.test.ts @@ -8,68 +8,68 @@ import { getUserByIdCommandBuilder } from './getUserByIdCommandBuilder.js' import type { UserV1GetUserByIdInputParameter, UserV1GetUserByIdInputPayload } from './types.js' describe('service User version 1 - command getUserById', () => { - let sandbox = createSandbox() - beforeEach(() => { - sandbox = createSandbox() - }) + let sandbox = createSandbox() + beforeEach(() => { + sandbox = createSandbox() + }) - afterEach(() => { - sandbox.restore() - }) + afterEach(() => { + sandbox.restore() + }) - test('returns a user', async () => { - const service = await userV1Service.getInstance(getEventBridgeMock(sandbox).mock, { - logger: getLoggerMock(sandbox).mock, - }) + test('returns a user', async () => { + const service = await userV1Service.getInstance(getEventBridgeMock(sandbox).mock, { + logger: getLoggerMock(sandbox).mock, + }) - const getUserById = safeBind(getUserByIdCommandBuilder.getCommandFunction(), service) + const getUserById = safeBind(getUserByIdCommandBuilder.getCommandFunction(), service) - const payload: UserV1GetUserByIdInputPayload = undefined + const payload: UserV1GetUserByIdInputPayload = undefined - const userMock: User = { - email: 'email@example.com', - name: 'test user', - password: 'password', - userId: 'a5fef052-911c-472c-ac25-e2da327f0af5', - } + const userMock: User = { + email: 'email@example.com', + name: 'test user', + password: 'password', + userId: 'a5fef052-911c-472c-ac25-e2da327f0af5', + } - const parameter: UserV1GetUserByIdInputParameter = { - userId: userMock.userId, - } + const parameter: UserV1GetUserByIdInputParameter = { + userId: userMock.userId, + } - const context = getUserByIdCommandBuilder.getCommandContextMock(payload, parameter, sandbox) + const context = getUserByIdCommandBuilder.getCommandContextMock({ payload, parameter, sandbox }) - context.stubs.getState.resolves({ [StateStoreKey.Users]: [userMock] }) + context.stubs.getState.resolves({ [StateStoreKey.Users]: [userMock] }) - const result = await getUserById(context.mock, payload, parameter) + const result = await getUserById(context.mock, payload, parameter) - expect(result).toStrictEqual({ email: userMock.email, name: userMock.name, userId: userMock.userId }) - }) + expect(result).toStrictEqual({ email: userMock.email, name: userMock.name, userId: userMock.userId }) + }) - test('throws if user can not be found', async () => { - const service = await userV1Service.getInstance(getEventBridgeMock(sandbox).mock, { - logger: getLoggerMock(sandbox).mock, - }) + test('throws if user can not be found', async () => { + const service = await userV1Service.getInstance(getEventBridgeMock(sandbox).mock, { + logger: getLoggerMock(sandbox).mock, + }) - const getUserById = safeBind(getUserByIdCommandBuilder.getCommandFunction(), service) + const getUserById = safeBind(getUserByIdCommandBuilder.getCommandFunction(), service) - const payload: UserV1GetUserByIdInputPayload = undefined + const payload: UserV1GetUserByIdInputPayload = undefined - const userMock: User = { - email: 'email@example.com', - name: 'test user', - password: 'password', - userId: 'a5fef052-911c-472c-ac25-e2da327f0af5', - } + const userMock: User = { + email: 'email@example.com', + name: 'test user', + password: 'password', + userId: 'a5fef052-911c-472c-ac25-e2da327f0af5', + } - const parameter: UserV1GetUserByIdInputParameter = { - userId: userMock.userId, - } + const parameter: UserV1GetUserByIdInputParameter = { + userId: userMock.userId, + } - const context = getUserByIdCommandBuilder.getCommandContextMock(payload, parameter, sandbox) + const context = getUserByIdCommandBuilder.getCommandContextMock({ payload, parameter, sandbox }) - context.stubs.getState.resolves({ [StateStoreKey.Users]: [] }) + context.stubs.getState.resolves({ [StateStoreKey.Users]: [] }) - await expect(getUserById(context.mock, payload, parameter)).rejects.toThrow('user could not be found') - }) + await expect(getUserById(context.mock, payload, parameter)).rejects.toThrow('user could not be found') + }) }) diff --git a/examples/nats-bridge/src/service/user/v1/command/getUserById/getUserByIdCommandBuilder.ts b/examples/nats-bridge/src/service/user/v1/command/getUserById/getUserByIdCommandBuilder.ts index 8d8e25d5f..5256e3242 100644 --- a/examples/nats-bridge/src/service/user/v1/command/getUserById/getUserByIdCommandBuilder.ts +++ b/examples/nats-bridge/src/service/user/v1/command/getUserById/getUserByIdCommandBuilder.ts @@ -4,26 +4,26 @@ import type { User } from '../../../../../types/index.js' import { StateStoreKey } from '../../../../../types/index.js' import { userV1ServiceBuilder } from '../../userV1ServiceBuilder.js' import { - userV1GetUserByIdInputParameterSchema, - userV1GetUserByIdInputPayloadSchema, - userV1GetUserByIdOutputPayloadSchema, + userV1GetUserByIdInputParameterSchema, + userV1GetUserByIdInputPayloadSchema, + userV1GetUserByIdOutputPayloadSchema, } from './schema.js' export const getUserByIdCommandBuilder = userV1ServiceBuilder - .getCommandBuilder('getUserById', 'returns the user given by the user id') - .addPayloadSchema(userV1GetUserByIdInputPayloadSchema) - .addParameterSchema(userV1GetUserByIdInputParameterSchema) - .addOutputSchema(userV1GetUserByIdOutputPayloadSchema) - .exposeAsHttpEndpoint('GET', 'user/:userId') - .setCommandFunction(async function (context, _payload, parameter) { - const result = (await context.states.getState(StateStoreKey.Users)) as { [StateStoreKey.Users]: User[] | undefined } - const users = result.users ?? [] + .getCommandBuilder('getUserById', 'returns the user given by the user id') + .addPayloadSchema(userV1GetUserByIdInputPayloadSchema) + .addParameterSchema(userV1GetUserByIdInputParameterSchema) + .addOutputSchema(userV1GetUserByIdOutputPayloadSchema) + .exposeAsHttpEndpoint('GET', 'user/:userId') + .setCommandFunction(async function (context, _payload, parameter) { + const result = (await context.states.getState(StateStoreKey.Users)) as { [StateStoreKey.Users]: User[] | undefined } + const users = result.users ?? [] - const user = users.find((user) => (user.userId = parameter.userId)) + const user = users.find(user => user.userId === parameter.userId) - if (!user) { - throw new HandledError(StatusCode.NotFound, 'user could not be found', { userId: parameter.userId }) - } + if (!user) { + throw new HandledError(StatusCode.NotFound, 'user could not be found', { userId: parameter.userId }) + } - return user - }) + return user + }) diff --git a/examples/nats-bridge/src/service/user/v1/command/getUserById/schema.ts b/examples/nats-bridge/src/service/user/v1/command/getUserById/schema.ts index 87b2549bb..06a7616ec 100644 --- a/examples/nats-bridge/src/service/user/v1/command/getUserById/schema.ts +++ b/examples/nats-bridge/src/service/user/v1/command/getUserById/schema.ts @@ -3,10 +3,10 @@ import { z } from 'zod' // define the input parameters export const userV1GetUserByIdInputParameterSchema = extendApi( - z.object({ - userId: extendApi(z.string().uuid(), { title: 'the user id', example: 'a5fef052-911c-472c-ac25-e2da327f0af5' }), - }), - { title: 'get user by id input payload schema' }, + z.object({ + userId: extendApi(z.string().uuid(), { title: 'the user id', example: 'a5fef052-911c-472c-ac25-e2da327f0af5' }), + }), + { title: 'get user by id input payload schema' }, ) // define the input payload @@ -14,13 +14,13 @@ export const userV1GetUserByIdInputPayloadSchema = z.undefined() // define the output payload export const userV1GetUserByIdOutputPayloadSchema = extendApi( - z.object({ - userId: extendApi(z.string().uuid(), { title: 'the user id', example: 'a5fef052-911c-472c-ac25-e2da327f0af5' }), - email: extendApi(z.string().email(), { title: 'the email of the user to register', example: 'user@email.com' }), - name: extendApi(z.string().min(3), { - title: 'the name of the user to register', - example: 'User', - }), - }), - { title: 'the sign up input' }, + z.object({ + userId: extendApi(z.string().uuid(), { title: 'the user id', example: 'a5fef052-911c-472c-ac25-e2da327f0af5' }), + email: extendApi(z.string().email(), { title: 'the email of the user to register', example: 'user@email.com' }), + name: extendApi(z.string().min(3), { + title: 'the name of the user to register', + example: 'User', + }), + }), + { title: 'the sign up input' }, ) diff --git a/examples/nats-bridge/src/service/user/v1/command/getUserById/types.ts b/examples/nats-bridge/src/service/user/v1/command/getUserById/types.ts index 7a3c6290c..59d70c337 100644 --- a/examples/nats-bridge/src/service/user/v1/command/getUserById/types.ts +++ b/examples/nats-bridge/src/service/user/v1/command/getUserById/types.ts @@ -1,9 +1,9 @@ import type { z } from 'zod' import type { - userV1GetUserByIdInputParameterSchema, - userV1GetUserByIdInputPayloadSchema, - userV1GetUserByIdOutputPayloadSchema, + userV1GetUserByIdInputParameterSchema, + userV1GetUserByIdInputPayloadSchema, + userV1GetUserByIdOutputPayloadSchema, } from './schema.js' export type UserV1GetUserByIdInputParameter = z.input diff --git a/examples/nats-bridge/src/service/user/v1/command/signUp/schema.ts b/examples/nats-bridge/src/service/user/v1/command/signUp/schema.ts index f969055f8..17d29b986 100644 --- a/examples/nats-bridge/src/service/user/v1/command/signUp/schema.ts +++ b/examples/nats-bridge/src/service/user/v1/command/signUp/schema.ts @@ -6,24 +6,24 @@ export const userV1SignUpInputParameterSchema = extendApi(z.object({}), { title: // define the input payload export const userV1SignUpInputPayloadSchema = extendApi( - z.object({ - email: extendApi(z.string().email(), { title: 'the email of the user to register', example: 'user@email.com' }), - name: extendApi(z.string().min(3), { - title: 'the name of the user to register', - example: 'User', - }), - password: extendApi(z.string().min(3), { - title: 'the name login password', - example: 'password', - }), - }), - { title: 'the sign up input' }, + z.object({ + email: extendApi(z.string().email(), { title: 'the email of the user to register', example: 'user@email.com' }), + name: extendApi(z.string().min(3), { + title: 'the name of the user to register', + example: 'User', + }), + password: extendApi(z.string().min(3), { + title: 'the name login password', + example: 'password', + }), + }), + { title: 'the sign up input' }, ) // define the output payload export const userV1SignUpOutputPayloadSchema = extendApi( - z.object({ - userId: extendApi(z.string().uuid(), { title: 'the user id', example: 'a5fef052-911c-472c-ac25-e2da327f0af5' }), - }), - { title: 'sign up output payload schema' }, + z.object({ + userId: extendApi(z.string().uuid(), { title: 'the user id', example: 'a5fef052-911c-472c-ac25-e2da327f0af5' }), + }), + { title: 'sign up output payload schema' }, ) diff --git a/examples/nats-bridge/src/service/user/v1/command/signUp/signUp.test.ts b/examples/nats-bridge/src/service/user/v1/command/signUp/signUp.test.ts index 5b4f31157..31bac83af 100644 --- a/examples/nats-bridge/src/service/user/v1/command/signUp/signUp.test.ts +++ b/examples/nats-bridge/src/service/user/v1/command/signUp/signUp.test.ts @@ -7,69 +7,69 @@ import { signUpCommandBuilder } from './signUpCommandBuilder.js' import type { UserV1SignUpInputParameter, UserV1SignUpInputPayload } from './types.js' describe('service User version 1 - command signUp', () => { - let sandbox = createSandbox() - beforeEach(() => { - sandbox = createSandbox() - }) + let sandbox = createSandbox() + beforeEach(() => { + sandbox = createSandbox() + }) - afterEach(() => { - sandbox.restore() - }) + afterEach(() => { + sandbox.restore() + }) - test('can register a new user', async () => { - const service = await userV1Service.getInstance(getEventBridgeMock(sandbox).mock, { - logger: getLoggerMock(sandbox).mock, - }) + test('can register a new user', async () => { + const service = await userV1Service.getInstance(getEventBridgeMock(sandbox).mock, { + logger: getLoggerMock(sandbox).mock, + }) - const signUp = safeBind(signUpCommandBuilder.getCommandFunction(), service) + const signUp = safeBind(signUpCommandBuilder.getCommandFunction(), service) - const payload: UserV1SignUpInputPayload = { - name: 'test user', - email: 'email@example.com', - password: 'password', - } + const payload: UserV1SignUpInputPayload = { + name: 'test user', + email: 'email@example.com', + password: 'password', + } - const parameter: UserV1SignUpInputParameter = {} + const parameter: UserV1SignUpInputParameter = {} - const context = signUpCommandBuilder.getCommandContextMock(payload, parameter, sandbox) + const context = signUpCommandBuilder.getCommandContextMock({ payload, parameter, sandbox }) - context.stubs.getState.resolves({}) - context.stubs.setState.resolves() + context.stubs.getState.resolves({}) + context.stubs.setState.resolves() - const result = await signUp(context.mock, payload, parameter) + const result = await signUp(context.mock, payload, parameter) - expect(result.userId).toBeDefined() - }) + expect(result.userId).toBeDefined() + }) - test('throws when a user with same email exist', async () => { - const service = await userV1Service.getInstance(getEventBridgeMock(sandbox).mock, { - logger: getLoggerMock(sandbox).mock, - }) + test('throws when a user with same email exist', async () => { + const service = await userV1Service.getInstance(getEventBridgeMock(sandbox).mock, { + logger: getLoggerMock(sandbox).mock, + }) - const signUp = safeBind(signUpCommandBuilder.getCommandFunction(), service) + const signUp = safeBind(signUpCommandBuilder.getCommandFunction(), service) - const payload: UserV1SignUpInputPayload = { - name: 'test user', - email: 'email@example.com', - password: 'password', - } + const payload: UserV1SignUpInputPayload = { + name: 'test user', + email: 'email@example.com', + password: 'password', + } - const parameter: UserV1SignUpInputParameter = {} + const parameter: UserV1SignUpInputParameter = {} - const context = signUpCommandBuilder.getCommandContextMock(payload, parameter, sandbox) + const context = signUpCommandBuilder.getCommandContextMock({ payload, parameter, sandbox }) - context.stubs.getState.resolves({ - [StateStoreKey.Users]: [ - { - name: 'test user', - email: 'email@example.com', - password: 'password', - userId: 'a5fef052-911c-472c-ac25-e2da327f0af5', - }, - ], - }) - context.stubs.setState.resolves() + context.stubs.getState.resolves({ + [StateStoreKey.Users]: [ + { + name: 'test user', + email: 'email@example.com', + password: 'password', + userId: 'a5fef052-911c-472c-ac25-e2da327f0af5', + }, + ], + }) + context.stubs.setState.resolves() - await expect(signUp(context.mock, payload, parameter)).rejects.toThrow('the user already exists') - }) + await expect(signUp(context.mock, payload, parameter)).rejects.toThrow('the user already exists') + }) }) diff --git a/examples/nats-bridge/src/service/user/v1/command/signUp/signUpCommandBuilder.ts b/examples/nats-bridge/src/service/user/v1/command/signUp/signUpCommandBuilder.ts index b44bbb80c..729c69670 100644 --- a/examples/nats-bridge/src/service/user/v1/command/signUp/signUpCommandBuilder.ts +++ b/examples/nats-bridge/src/service/user/v1/command/signUp/signUpCommandBuilder.ts @@ -7,37 +7,37 @@ import { StateStoreKey } from '../../../../../types/index.js' import { ServiceEvent } from '../../../../ServiceEvent.enum.js' import { userV1ServiceBuilder } from '../../userV1ServiceBuilder.js' import { - userV1SignUpInputParameterSchema, - userV1SignUpInputPayloadSchema, - userV1SignUpOutputPayloadSchema, + userV1SignUpInputParameterSchema, + userV1SignUpInputPayloadSchema, + userV1SignUpOutputPayloadSchema, } from './schema.js' export const signUpCommandBuilder = userV1ServiceBuilder - .getCommandBuilder('signUp', 'registers a new user at our product') - .setSuccessEventName(ServiceEvent.NewUserRegistered) - .addPayloadSchema(userV1SignUpInputPayloadSchema) - .addParameterSchema(userV1SignUpInputParameterSchema) - .addOutputSchema(userV1SignUpOutputPayloadSchema) - .exposeAsHttpEndpoint('POST', 'user/signup') - .setCommandFunction(async function (context, payload, _parameter) { - const result = (await context.states.getState(StateStoreKey.Users)) as { [StateStoreKey.Users]: User[] | undefined } - - if (result.users?.some((user) => user.email === payload.email)) { - throw new HandledError(StatusCode.BadRequest, 'the user already exists') - } - - const user: User = { - ...payload, - userId: randomUUID(), - } - - const users = result.users ?? [] - users.push(user) - - await context.states.setState(StateStoreKey.Users, users) - - // add your business logic here - context.logger.info('new user added') - - return user - }) + .getCommandBuilder('signUp', 'registers a new user at our product') + .setSuccessEventName(ServiceEvent.NewUserRegistered) + .addPayloadSchema(userV1SignUpInputPayloadSchema) + .addParameterSchema(userV1SignUpInputParameterSchema) + .addOutputSchema(userV1SignUpOutputPayloadSchema) + .exposeAsHttpEndpoint('POST', 'user/signup') + .setCommandFunction(async function (context, payload, _parameter) { + const result = (await context.states.getState(StateStoreKey.Users)) as { [StateStoreKey.Users]: User[] | undefined } + + if (result.users?.some(user => user.email === payload.email)) { + throw new HandledError(StatusCode.BadRequest, 'the user already exists') + } + + const user: User = { + ...payload, + userId: randomUUID(), + } + + const users = result.users ?? [] + users.push(user) + + await context.states.setState(StateStoreKey.Users, users) + + // add your business logic here + context.logger.info('new user added') + + return user + }) diff --git a/examples/nats-bridge/src/service/user/v1/command/signUp/types.ts b/examples/nats-bridge/src/service/user/v1/command/signUp/types.ts index 084667e07..a162fc0d0 100644 --- a/examples/nats-bridge/src/service/user/v1/command/signUp/types.ts +++ b/examples/nats-bridge/src/service/user/v1/command/signUp/types.ts @@ -1,9 +1,9 @@ import type { z } from 'zod' import type { - userV1SignUpInputParameterSchema, - userV1SignUpInputPayloadSchema, - userV1SignUpOutputPayloadSchema, + userV1SignUpInputParameterSchema, + userV1SignUpInputPayloadSchema, + userV1SignUpOutputPayloadSchema, } from './schema.js' export type UserV1SignUpInputParameter = z.input diff --git a/examples/nats-bridge/src/service/user/v1/userV1Service.test.ts b/examples/nats-bridge/src/service/user/v1/userV1Service.test.ts index 47603a4b9..7efe0970c 100644 --- a/examples/nats-bridge/src/service/user/v1/userV1Service.test.ts +++ b/examples/nats-bridge/src/service/user/v1/userV1Service.test.ts @@ -1,11 +1,7 @@ import { userV1Service as service } from './userV1Service.js' describe('service user version 1', () => { - it('has valid commands', () => { - service.validateCommandDefinitions() - }) - - it('has valid subscriptions', () => { - service.validateSubscriptionDefinitions() - }) + it('has valid configuration', () => { + service.testServiceSetup() + }) }) diff --git a/examples/nats-bridge/src/service/user/v1/userV1Service.ts b/examples/nats-bridge/src/service/user/v1/userV1Service.ts index b483dcc51..2d972c27b 100644 --- a/examples/nats-bridge/src/service/user/v1/userV1Service.ts +++ b/examples/nats-bridge/src/service/user/v1/userV1Service.ts @@ -10,13 +10,13 @@ import { userV1ServiceBuilder } from './userV1ServiceBuilder.js' // other service config should be done in ./userServiceBuilder.ts file const commandDefinitions: CommandDefinitionList = [ - signUpCommandBuilder.getDefinition(), - getUserByIdCommandBuilder.getDefinition(), - getAllUsersCommandBuilder.getDefinition(), + signUpCommandBuilder.getDefinition(), + getUserByIdCommandBuilder.getDefinition(), + getAllUsersCommandBuilder.getDefinition(), ] const subscriptionDefinitions: SubscriptionDefinitionList = [] export const userV1Service = userV1ServiceBuilder - .addCommandDefinition(...commandDefinitions) - .addSubscriptionDefinition(...subscriptionDefinitions) + .addCommandDefinition(...commandDefinitions) + .addSubscriptionDefinition(...subscriptionDefinitions) diff --git a/examples/nats-bridge/src/service/user/v1/userV1ServiceBuilder.ts b/examples/nats-bridge/src/service/user/v1/userV1ServiceBuilder.ts index b047cf552..57a76d32d 100644 --- a/examples/nats-bridge/src/service/user/v1/userV1ServiceBuilder.ts +++ b/examples/nats-bridge/src/service/user/v1/userV1ServiceBuilder.ts @@ -5,12 +5,10 @@ import { generalUserServiceInfo } from '../generalUserServiceInfo.js' import { userServiceV1ConfigSchema } from './userServiceConfig.js' export const userServiceInfo = { - serviceVersion: '1', - ...generalUserServiceInfo, + serviceVersion: '1', + ...generalUserServiceInfo, } as const satisfies ServiceInfoType // create a service builder instance and assign service config schema and default config. -export const userV1ServiceBuilder = new ServiceBuilder(userServiceInfo) - .setConfigSchema(userServiceV1ConfigSchema) - .setDefaultConfig({}) +export const userV1ServiceBuilder = new ServiceBuilder(userServiceInfo).setConfigSchema(userServiceV1ConfigSchema) diff --git a/examples/nats-bridge/src/types/StateStoreKey.enum.ts b/examples/nats-bridge/src/types/StateStoreKey.enum.ts index 2d5e51b84..da42b4228 100644 --- a/examples/nats-bridge/src/types/StateStoreKey.enum.ts +++ b/examples/nats-bridge/src/types/StateStoreKey.enum.ts @@ -1,3 +1,3 @@ export const StateStoreKey = { - Users: 'users', + Users: 'users', } as const diff --git a/examples/nats-bridge/src/types/User.ts b/examples/nats-bridge/src/types/User.ts index 8e2616607..de6239fbf 100644 --- a/examples/nats-bridge/src/types/User.ts +++ b/examples/nats-bridge/src/types/User.ts @@ -1,6 +1,6 @@ export type User = { - email: string - name: string - password: string - userId: string + email: string + name: string + password: string + userId: string } diff --git a/examples/nats-bridge/tsconfig.json b/examples/nats-bridge/tsconfig.json index a84ca294e..7d9abe7a2 100644 --- a/examples/nats-bridge/tsconfig.json +++ b/examples/nats-bridge/tsconfig.json @@ -1,11 +1,8 @@ { - "extends": "../../tsconfig.json", - - "compilerOptions": { - "outDir": "./build" - }, - "files": [ - "src/index.ts", - "fastify.d.ts" - ], -} \ No newline at end of file + "extends": "../../tsconfig.json", + + "compilerOptions": { + "outDir": "./build" + }, + "files": ["src/index.ts", "fastify.d.ts"] +} diff --git a/examples/quickstart/package.json b/examples/quickstart/package.json index 9f14d8324..49da0986d 100644 --- a/examples/quickstart/package.json +++ b/examples/quickstart/package.json @@ -1,37 +1,37 @@ { - "name": "@purista/quickstart", - "version": "1.11.0", - "description": "purista backend framework", - "homepage": "https://purista.dev", - "private": true, - "repository": { - "type": "git", - "url": "git@github.com:sebastianwessel/purista.git" - }, - "author": "Sebastian Wessel", - "license": "ISC", - "main": "src/index.ts", - "type": "module", - "engines": { - "node": ">=18.15" - }, - "scripts": { - "start": "tsx src/index.ts | pino-pretty", - "lint": "eslint . --ext .ts,.json --cache . --fix", - "test": "vitest" - }, - "devDependencies": { - "@types/node": "^20.11.17", - "pino-pretty": "^10.3.1", - "sinon": "^17.0.1", - "tsx": "^4.7.0", - "typescript": "^5.3.3", - "vitest": "^1.3.0" - }, - "dependencies": { - "@fastify/static": "^7.0.1", - "@purista/core": "*", - "@purista/httpserver": "*", - "zod": "3.22.4" - } + "name": "@purista/quickstart", + "version": "1.11.0", + "description": "purista backend framework", + "homepage": "https://purista.dev", + "private": true, + "repository": { + "type": "git", + "url": "git@github.com:puristajs/purista.git" + }, + "author": "Sebastian Wessel", + "license": "ISC", + "main": "src/index.ts", + "type": "module", + "engines": { + "node": ">=18.15" + }, + "scripts": { + "start": "tsx src/index.ts | pino-pretty", + "lint": "npx @biomejs/biome check --write", + "test": "vitest" + }, + "devDependencies": { + "@types/node": "^22.5.1", + "pino-pretty": "^13.0.0", + "sinon": "^19.0.2", + "tsx": "^4.19.0", + "typescript": "^5.5.4", + "vitest": "^3.0.4" + }, + "dependencies": { + "@fastify/static": "^8.0.3", + "@purista/core": "*", + "@purista/httpserver": "*", + "zod": "^3.24.1" + } } diff --git a/examples/quickstart/src/index.ts b/examples/quickstart/src/index.ts index c086e20d4..3f7adc70d 100644 --- a/examples/quickstart/src/index.ts +++ b/examples/quickstart/src/index.ts @@ -4,18 +4,18 @@ import { httpServerV1Service } from '@purista/httpserver' import { pingV1Service } from './service/ping/v1/index.js' export const main = async () => { - // initiate the event bridge as first step - const eventBridge = new DefaultEventBridge() - await eventBridge.start() - // initiate the webserver service as second step - const httpServerService = await httpServerV1Service.getInstance(eventBridge) + // initiate the event bridge as first step + const eventBridge = new DefaultEventBridge() + await eventBridge.start() + // initiate the webserver service as second step + const httpServerService = await httpServerV1Service.getInstance(eventBridge) - // start the webserver - await httpServerService.start() + // start the webserver + await httpServerService.start() - // add your service - const pingService = await pingV1Service.getInstance(eventBridge) - await pingService.start() + // add your service + const pingService = await pingV1Service.getInstance(eventBridge) + await pingService.start() } main() diff --git a/examples/quickstart/src/service/ServiceEvent.enum.ts b/examples/quickstart/src/service/ServiceEvent.enum.ts index 875bffb27..415d9a061 100644 --- a/examples/quickstart/src/service/ServiceEvent.enum.ts +++ b/examples/quickstart/src/service/ServiceEvent.enum.ts @@ -1,7 +1,7 @@ export enum ServiceEvent { - /** - * Emitted by ping v1 command ping: - * the ping command exposed as http endpoint - */ - Pinged = 'pinged', + /** + * Emitted by ping v1 command ping: + * the ping command exposed as http endpoint + */ + Pinged = 'pinged', } diff --git a/examples/quickstart/src/service/ping/generalPingServiceInfo.ts b/examples/quickstart/src/service/ping/generalPingServiceInfo.ts index 91b5b8d14..6cc10b051 100644 --- a/examples/quickstart/src/service/ping/generalPingServiceInfo.ts +++ b/examples/quickstart/src/service/ping/generalPingServiceInfo.ts @@ -1,6 +1,6 @@ import type { ServiceInfoType } from '@purista/core' export const generalPingServiceInfo: Omit = { - serviceName: 'Ping', - serviceDescription: 'Example ping service', + serviceName: 'Ping', + serviceDescription: 'Example ping service', } diff --git a/examples/quickstart/src/service/ping/v1/command/ping/ping.test.ts b/examples/quickstart/src/service/ping/v1/command/ping/ping.test.ts index a9676004b..bad16af95 100644 --- a/examples/quickstart/src/service/ping/v1/command/ping/ping.test.ts +++ b/examples/quickstart/src/service/ping/v1/command/ping/ping.test.ts @@ -6,30 +6,30 @@ import { pingCommandBuilder } from './pingCommandBuilder.js' import type { PingV1PingInputParameter, PingV1PingInputPayload } from './types.js' describe('service Ping version 1 - command ping', () => { - let sandbox = createSandbox() - beforeEach(() => { - sandbox = createSandbox() - }) + let sandbox = createSandbox() + beforeEach(() => { + sandbox = createSandbox() + }) - afterEach(() => { - sandbox.restore() - }) + afterEach(() => { + sandbox.restore() + }) - test('does not throw', async () => { - const service = await pingV1Service.getInstance(getEventBridgeMock(sandbox).mock, { - logger: getLoggerMock(sandbox).mock, - }) + test('does not throw', async () => { + const service = await pingV1Service.getInstance(getEventBridgeMock(sandbox).mock, { + logger: getLoggerMock(sandbox).mock, + }) - const ping = safeBind(pingCommandBuilder.getCommandFunction(), service) + const ping = safeBind(pingCommandBuilder.getCommandFunction(), service) - const payload: PingV1PingInputPayload = { ping: 'test' } + const payload: PingV1PingInputPayload = { ping: 'test' } - const parameter: PingV1PingInputParameter = {} + const parameter: PingV1PingInputParameter = {} - const context = pingCommandBuilder.getCommandContextMock(payload, parameter, sandbox) + const context = pingCommandBuilder.getCommandContextMock({ payload, parameter, sandbox }) - const result = await ping(context.mock, payload, parameter) + const result = await ping(context.mock, payload, parameter) - expect(result).toStrictEqual({ pong: 'test' }) - }) + expect(result).toStrictEqual({ pong: 'test' }) + }) }) diff --git a/examples/quickstart/src/service/ping/v1/command/ping/pingCommandBuilder.ts b/examples/quickstart/src/service/ping/v1/command/ping/pingCommandBuilder.ts index 26f62580a..e221725ad 100644 --- a/examples/quickstart/src/service/ping/v1/command/ping/pingCommandBuilder.ts +++ b/examples/quickstart/src/service/ping/v1/command/ping/pingCommandBuilder.ts @@ -1,21 +1,21 @@ import { ServiceEvent } from '../../../../ServiceEvent.enum.js' import { pingV1ServiceBuilder } from '../../pingV1ServiceBuilder.js' import { - pingV1PingInputParameterSchema, - pingV1PingInputPayloadSchema, - pingV1PingOutputPayloadSchema, + pingV1PingInputParameterSchema, + pingV1PingInputPayloadSchema, + pingV1PingOutputPayloadSchema, } from './schema.js' export const pingCommandBuilder = pingV1ServiceBuilder - .getCommandBuilder('ping', 'the ping command exposed as http endpoint') - .setSuccessEventName(ServiceEvent.Pinged) - .addPayloadSchema(pingV1PingInputPayloadSchema) - .addParameterSchema(pingV1PingInputParameterSchema) - .addOutputSchema(pingV1PingOutputPayloadSchema) - .exposeAsHttpEndpoint('POST', 'ping') - .setCommandFunction(async function (_context, payload, _parameter) { - // add your business logic here - return { - pong: payload.ping, - } - }) + .getCommandBuilder('ping', 'the ping command exposed as http endpoint') + .setSuccessEventName(ServiceEvent.Pinged) + .addPayloadSchema(pingV1PingInputPayloadSchema) + .addParameterSchema(pingV1PingInputParameterSchema) + .addOutputSchema(pingV1PingOutputPayloadSchema) + .exposeAsHttpEndpoint('POST', 'ping') + .setCommandFunction(async function (_context, payload, _parameter) { + // add your business logic here + return { + pong: payload.ping, + } + }) diff --git a/examples/quickstart/src/service/ping/v1/command/ping/schema.ts b/examples/quickstart/src/service/ping/v1/command/ping/schema.ts index 54be6bf2b..b69f6b247 100644 --- a/examples/quickstart/src/service/ping/v1/command/ping/schema.ts +++ b/examples/quickstart/src/service/ping/v1/command/ping/schema.ts @@ -6,16 +6,16 @@ export const pingV1PingInputParameterSchema = extendApi(z.object({}), { title: ' // define the input payload export const pingV1PingInputPayloadSchema = extendApi( - z.object({ - ping: extendApi(z.string(), { title: 'Ping input' }), - }), - { title: 'ping input payload schema' }, + z.object({ + ping: extendApi(z.string(), { title: 'Ping input' }), + }), + { title: 'ping input payload schema' }, ) // define the output payload export const pingV1PingOutputPayloadSchema = extendApi( - z.object({ - pong: extendApi(z.string(), { title: 'Pong output' }), - }), - { title: 'ping output payload schema' }, + z.object({ + pong: extendApi(z.string(), { title: 'Pong output' }), + }), + { title: 'ping output payload schema' }, ) diff --git a/examples/quickstart/src/service/ping/v1/command/ping/types.ts b/examples/quickstart/src/service/ping/v1/command/ping/types.ts index eacdcc1f8..db48417b5 100644 --- a/examples/quickstart/src/service/ping/v1/command/ping/types.ts +++ b/examples/quickstart/src/service/ping/v1/command/ping/types.ts @@ -1,9 +1,9 @@ import type { z } from 'zod' import type { - pingV1PingInputParameterSchema, - pingV1PingInputPayloadSchema, - pingV1PingOutputPayloadSchema, + pingV1PingInputParameterSchema, + pingV1PingInputPayloadSchema, + pingV1PingOutputPayloadSchema, } from './schema.js' export type PingV1PingInputParameter = z.input diff --git a/examples/quickstart/src/service/ping/v1/pingV1Service.test.ts b/examples/quickstart/src/service/ping/v1/pingV1Service.test.ts index 977f69d99..cfd4e45f4 100644 --- a/examples/quickstart/src/service/ping/v1/pingV1Service.test.ts +++ b/examples/quickstart/src/service/ping/v1/pingV1Service.test.ts @@ -1,11 +1,7 @@ import { pingV1Service as service } from './pingV1Service.js' describe('service ping version 1', () => { - it('has valid commands', () => { - service.validateCommandDefinitions() - }) - - it('has valid subscriptions', () => { - service.validateSubscriptionDefinitions() - }) + it('has valid configuration', () => { + service.testServiceSetup() + }) }) diff --git a/examples/quickstart/src/service/ping/v1/pingV1Service.ts b/examples/quickstart/src/service/ping/v1/pingV1Service.ts index 088d4724c..daf280a16 100644 --- a/examples/quickstart/src/service/ping/v1/pingV1Service.ts +++ b/examples/quickstart/src/service/ping/v1/pingV1Service.ts @@ -13,5 +13,5 @@ const commandDefinitions: CommandDefinitionList = [pingCommandBuilder.getDe const subscriptionDefinitions: SubscriptionDefinitionList = [logSubscriptionBuilder.getDefinition()] export const pingV1Service = pingV1ServiceBuilder - .addCommandDefinition(...commandDefinitions) - .addSubscriptionDefinition(...subscriptionDefinitions) + .addCommandDefinition(...commandDefinitions) + .addSubscriptionDefinition(...subscriptionDefinitions) diff --git a/examples/quickstart/src/service/ping/v1/pingV1ServiceBuilder.ts b/examples/quickstart/src/service/ping/v1/pingV1ServiceBuilder.ts index 7ad735f14..9233f2f37 100644 --- a/examples/quickstart/src/service/ping/v1/pingV1ServiceBuilder.ts +++ b/examples/quickstart/src/service/ping/v1/pingV1ServiceBuilder.ts @@ -5,12 +5,10 @@ import { generalPingServiceInfo } from '../generalPingServiceInfo.js' import { pingServiceV1ConfigSchema } from './pingServiceConfig.js' export const pingServiceInfo = { - serviceVersion: '1', - ...generalPingServiceInfo, + serviceVersion: '1', + ...generalPingServiceInfo, } as const satisfies ServiceInfoType // create a service builder instance and assign service config schema and default config. -export const pingV1ServiceBuilder = new ServiceBuilder(pingServiceInfo) - .setConfigSchema(pingServiceV1ConfigSchema) - .setDefaultConfig({}) +export const pingV1ServiceBuilder = new ServiceBuilder(pingServiceInfo).setConfigSchema(pingServiceV1ConfigSchema) diff --git a/examples/quickstart/src/service/ping/v1/subscription/log/log.test.ts b/examples/quickstart/src/service/ping/v1/subscription/log/log.test.ts index 656926b58..90a29cbaf 100644 --- a/examples/quickstart/src/service/ping/v1/subscription/log/log.test.ts +++ b/examples/quickstart/src/service/ping/v1/subscription/log/log.test.ts @@ -6,41 +6,41 @@ import { logSubscriptionBuilder } from './logSubscriptionBuilder.js' import type { PingV1LogInputPayload } from './types.js' describe('service Ping version 1 - subscription log', () => { - let sandbox = createSandbox() - beforeEach(() => { - sandbox = createSandbox() - }) + let sandbox = createSandbox() + beforeEach(() => { + sandbox = createSandbox() + }) - afterEach(() => { - sandbox.restore() - }) + afterEach(() => { + sandbox.restore() + }) - test('does not throw', async () => { - // create a service instance to be bind to the subscription function - const service = await pingV1Service.getInstance(getEventBridgeMock(sandbox).mock, { - logger: getLoggerMock(sandbox).mock, - }) + test('does not throw', async () => { + // create a service instance to be bind to the subscription function + const service = await pingV1Service.getInstance(getEventBridgeMock(sandbox).mock, { + logger: getLoggerMock(sandbox).mock, + }) - // get the subscription function and bind to service instance to work properly - const log = safeBind(logSubscriptionBuilder.getSubscriptionFunction(), service) + // get the subscription function and bind to service instance to work properly + const log = safeBind(logSubscriptionBuilder.getSubscriptionFunction(), service) - // define the test input payload - const payload: PingV1LogInputPayload = { - pong: 'test', - } + // define the test input payload + const payload: PingV1LogInputPayload = { + pong: 'test', + } - // define the test input parameter - const parameter = undefined + // define the test input parameter + const parameter = undefined - // create a mock message with the expected input for the subscription function - const message = getCommandSuccessMessageMock(payload) + // create a mock message with the expected input for the subscription function + const message = getCommandSuccessMessageMock(payload) - // create a subscription context for the subscription function - const context = logSubscriptionBuilder.getSubscriptionContextMock(message, sandbox) + // create a subscription context for the subscription function + const context = logSubscriptionBuilder.getSubscriptionContextMock({ message, sandbox }) - // execute the subscription function - const result = await log(context.mock, payload, parameter as any) + // execute the subscription function + const result = await log(context.mock, payload, parameter as any) - expect(result).toBeUndefined() - }) + expect(result).toBeUndefined() + }) }) diff --git a/examples/quickstart/src/service/ping/v1/subscription/log/logSubscriptionBuilder.ts b/examples/quickstart/src/service/ping/v1/subscription/log/logSubscriptionBuilder.ts index 52153625a..9ff7c41f3 100644 --- a/examples/quickstart/src/service/ping/v1/subscription/log/logSubscriptionBuilder.ts +++ b/examples/quickstart/src/service/ping/v1/subscription/log/logSubscriptionBuilder.ts @@ -3,10 +3,10 @@ import { pingV1ServiceBuilder } from '../../pingV1ServiceBuilder.js' import { pingV1LogInputPayloadSchema } from './schema.js' export const logSubscriptionBuilder = pingV1ServiceBuilder - .getSubscriptionBuilder('log', 'logs the ping events') - .subscribeToEvent(ServiceEvent.Pinged) - .addPayloadSchema(pingV1LogInputPayloadSchema) - .setSubscriptionFunction(async function (context, payload, _parameter) { - // add your business logic here - context.logger.info(`there was a ping responded with ${payload.pong}`) - }) + .getSubscriptionBuilder('log', 'logs the ping events') + .subscribeToEvent(ServiceEvent.Pinged) + .addPayloadSchema(pingV1LogInputPayloadSchema) + .setSubscriptionFunction(async function (context, payload, _parameter) { + // add your business logic here + context.logger.info(`there was a ping responded with ${payload.pong}`) + }) diff --git a/examples/quickstart/src/service/ping/v1/subscription/log/schema.ts b/examples/quickstart/src/service/ping/v1/subscription/log/schema.ts index 8421eae80..b4b583ac8 100644 --- a/examples/quickstart/src/service/ping/v1/subscription/log/schema.ts +++ b/examples/quickstart/src/service/ping/v1/subscription/log/schema.ts @@ -3,8 +3,8 @@ import { z } from 'zod' // define the input payload export const pingV1LogInputPayloadSchema = extendApi( - z.object({ - pong: extendApi(z.string(), { title: 'Pong' }), - }), - { title: 'pong payload schema' }, + z.object({ + pong: extendApi(z.string(), { title: 'Pong' }), + }), + { title: 'pong payload schema' }, ) diff --git a/examples/quickstart/tsconfig.json b/examples/quickstart/tsconfig.json index a84ca294e..7d9abe7a2 100644 --- a/examples/quickstart/tsconfig.json +++ b/examples/quickstart/tsconfig.json @@ -1,11 +1,8 @@ { - "extends": "../../tsconfig.json", - - "compilerOptions": { - "outDir": "./build" - }, - "files": [ - "src/index.ts", - "fastify.d.ts" - ], -} \ No newline at end of file + "extends": "../../tsconfig.json", + + "compilerOptions": { + "outDir": "./build" + }, + "files": ["src/index.ts", "fastify.d.ts"] +} diff --git a/examples/readme.md b/examples/readme.md index 9ea6f8c86..af281a4ac 100644 --- a/examples/readme.md +++ b/examples/readme.md @@ -8,7 +8,7 @@ The examples are referenced in the handbook at [purista.dev](https://purista.dev To try out the examples and to play around, please: -- clone this repository to your local machine `git clone git@github.com:sebastianwessel/purista.git` +- clone this repository to your local machine `git clone git@github.com:puristajs/purista.git` - in the local repository, in the root folder run `npm i` - than, navigate to the example folders and follow the steps provided in the readme file for each example. diff --git a/examples/temporal/package.json b/examples/temporal/package.json index 22b7abc7c..e0233c107 100644 --- a/examples/temporal/package.json +++ b/examples/temporal/package.json @@ -1,45 +1,44 @@ { - "name": "@purista/temporal-example", - "private": true, - "version": "1.11.0", - "description": "PURISTA blueprint project", - "engines": { - "node": ">=18" - }, - "main": "src/index.ts", - "type": "module", - "scripts": { - "start": "tsx src/index.ts | pino-pretty", - "dev": "tsx watch src/index.ts | pino-pretty", - "dev:worker": "tsx watch src/temporal/worker.ts workflow.watch", - "test": "vitest", - "lint": "eslint --ext .ts,.js,.json --cache .", - "lint:fix": "eslint . --ext .ts,.js,.json --fix", - "purista": "purista add" - }, - "dependencies": { - "@hono/node-server": "^1.8.0", - "@hono/swagger-ui": "^0.2.1", - "@opentelemetry/exporter-trace-otlp-http": "^0.48.0", - "@opentelemetry/sdk-trace-node": "^1.21.0", - "@purista/core": "*", - "@purista/hono-http-server": "*", - "@purista/natsbridge": "*", - "@temporalio/activity": "^1.9.1", - "@temporalio/client": "^1.9.1", - "@temporalio/interceptors-opentelemetry": "^1.9.1", - "@temporalio/worker": "^1.9.1", - "@temporalio/workflow": "^1.9.1", - "zod": "^3.22.4" - }, - "devDependencies": { - "tsx": "^4.7.1", - "typescript": "^5.3.3", - "@types/node": "^20.11.19", - "@types/sinon": "^17.0.3", - "pino-pretty": "^10.3.1", - "prettier": "^3.2.5", - "sinon": "^17.0.1", - "vitest": "^1.3.0" - } + "name": "@purista/temporal-example", + "private": true, + "version": "1.11.0", + "description": "PURISTA blueprint project", + "engines": { + "node": ">=18" + }, + "main": "src/index.ts", + "type": "module", + "scripts": { + "start": "tsx src/index.ts | pino-pretty", + "dev": "tsx watch src/index.ts | pino-pretty", + "dev:worker": "tsx watch src/temporal/worker.ts workflow.watch", + "test": "vitest", + "lint": "eslint --ext .ts,.js,.json --cache .", + "lint:fix": "eslint . --ext .ts,.js,.json --fix", + "purista": "purista add" + }, + "dependencies": { + "@hono/node-server": "^1.12.2", + "@hono/swagger-ui": "^0.5.0", + "@opentelemetry/exporter-trace-otlp-http": "^0.57.1", + "@opentelemetry/sdk-trace-node": "^1.26.0", + "@purista/core": "*", + "@purista/hono-http-server": "*", + "@purista/natsbridge": "*", + "@temporalio/activity": "^1.10.3", + "@temporalio/client": "^1.10.3", + "@temporalio/interceptors-opentelemetry": "^1.10.3", + "@temporalio/worker": "^1.10.3", + "@temporalio/workflow": "^1.10.3", + "zod": "^3.24.1" + }, + "devDependencies": { + "tsx": "^4.19.0", + "typescript": "^5.5.4", + "@types/node": "^22.5.1", + "@types/sinon": "^17.0.3", + "pino-pretty": "^13.0.0", + "sinon": "^19.0.2", + "vitest": "^3.0.4" + } } diff --git a/examples/temporal/src/config/jaegerExporterOptions.ts b/examples/temporal/src/config/jaegerExporterOptions.ts index 288f846f0..5390d4a20 100644 --- a/examples/temporal/src/config/jaegerExporterOptions.ts +++ b/examples/temporal/src/config/jaegerExporterOptions.ts @@ -1,5 +1,5 @@ const jaegerExporterOptions = { - url: `http://localhost:4318/v1/traces`, + url: 'http://localhost:4318/v1/traces', } export default jaegerExporterOptions diff --git a/examples/temporal/src/config/temporalConfig.ts b/examples/temporal/src/config/temporalConfig.ts index 36ccfb5e3..1159b2f3c 100644 --- a/examples/temporal/src/config/temporalConfig.ts +++ b/examples/temporal/src/config/temporalConfig.ts @@ -1,9 +1,9 @@ const temporalConfig = { - taskQueue: 'default-task-queue', - namespace: 'default', - connect: { - address: 'localhost:7233', - }, + taskQueue: 'default-task-queue', + namespace: 'default', + connect: { + address: 'localhost:7233', + }, } export default temporalConfig diff --git a/examples/temporal/src/index.ts b/examples/temporal/src/index.ts index bdd04ec5e..dab79b3e8 100644 --- a/examples/temporal/src/index.ts +++ b/examples/temporal/src/index.ts @@ -3,7 +3,7 @@ import { serveStatic } from '@hono/node-server/serve-static' import { swaggerUI } from '@hono/swagger-ui' import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http' import { SimpleSpanProcessor } from '@opentelemetry/sdk-trace-node' -import { gracefulShutdown, initLogger, type Service } from '@purista/core' +import { type Service, gracefulShutdown, initLogger } from '@purista/core' import { honoV1Service } from '@purista/hono-http-server' import { NatsBridge } from '@purista/natsbridge' import { compress } from 'hono/compress' @@ -17,79 +17,79 @@ import { emailV1Service } from './service/email/v1/emailV1Service.js' import { userV1Service } from './service/user/v1/userV1Service.js' export const main = async () => { - const logger = initLogger('debug') - - const exporter = new OTLPTraceExporter(jaegerExporterOptions) - - const spanProcessor = new SimpleSpanProcessor(exporter) - - const services: Service[] = [] - - // initiate the event bridge as first step - const eventBridge = new NatsBridge({ ...natsBridgeConfig, logger, spanProcessor }) - await eventBridge.start() - - // start the services - const userService = await userV1Service.getInstance(eventBridge, { - logger, - spanProcessor, - serviceConfig: { ...temporalConfig }, - }) - await userService.start() - services.push(userService) - - const emailService = await emailV1Service.getInstance(eventBridge, { logger, spanProcessor }) - await emailService.start() - services.push(emailService) - - const accountService = await accountV1Service.getInstance(eventBridge, { logger, spanProcessor }) - await accountService.start() - services.push(accountService) - - const cardService = await cardV1Service.getInstance(eventBridge, { logger, spanProcessor }) - await cardService.start() - services.push(cardService) - - // initiate the webserver service as second step - - const port = 3000 - - const honoService = await honoV1Service.getInstance(eventBridge, { - logger, - spanProcessor, - serviceConfig: { services }, - }) - honoService.app.use(compress()) - honoService.app.get('/api', swaggerUI({ url: '/api/openapi.json' })) - honoService.app.get('*', serveStatic({ root: './public' })) - honoService.openApi.addServer({ url: `http://localhost:${port}`, description: 'the local server' }) - - // start the webserver - await honoService.start() - - const serverInstance = serve({ - fetch: honoService.app.fetch, - port, - }) - - // add initiation and start of your services here - // const yourService = await yourServiceBuilder.getInstance(eventBridge) - // await yourService.start() - // services.push(yourService) - - // try to shut down as clean as possible - gracefulShutdown(logger, [ - honoService.prepareDestroy(), - eventBridge, - ...services, - { - name: `${honoService.serviceInfo.serviceName} ${honoService.serviceInfo.serviceVersion} close socket`, - destroy: async () => { - serverInstance.close() - }, - }, - honoService, - ]) + const logger = initLogger('debug') + + const exporter = new OTLPTraceExporter(jaegerExporterOptions) + + const spanProcessor = new SimpleSpanProcessor(exporter) + + const services: Service[] = [] + + // initiate the event bridge as first step + const eventBridge = new NatsBridge({ ...natsBridgeConfig, logger, spanProcessor }) + await eventBridge.start() + + // start the services + const userService = await userV1Service.getInstance(eventBridge, { + logger, + spanProcessor, + serviceConfig: { ...temporalConfig }, + }) + await userService.start() + services.push(userService) + + const emailService = await emailV1Service.getInstance(eventBridge, { logger, spanProcessor }) + await emailService.start() + services.push(emailService) + + const accountService = await accountV1Service.getInstance(eventBridge, { logger, spanProcessor }) + await accountService.start() + services.push(accountService) + + const cardService = await cardV1Service.getInstance(eventBridge, { logger, spanProcessor }) + await cardService.start() + services.push(cardService) + + // initiate the webserver service as second step + + const port = 3000 + + const honoService = await honoV1Service.getInstance(eventBridge, { + logger, + spanProcessor, + serviceConfig: { services }, + }) + honoService.app.use(compress()) + honoService.app.get('/api', swaggerUI({ url: '/api/openapi.json' })) + honoService.app.get('*', serveStatic({ root: './public' })) + honoService.openApi.addServer({ url: `http://localhost:${port}`, description: 'the local server' }) + + // start the webserver + await honoService.start() + + const serverInstance = serve({ + fetch: honoService.app.fetch, + port, + }) + + // add initiation and start of your services here + // const yourService = await yourServiceBuilder.getInstance(eventBridge) + // await yourService.start() + // services.push(yourService) + + // try to shut down as clean as possible + gracefulShutdown(logger, [ + honoService.prepareDestroy(), + eventBridge, + ...services, + { + name: `${honoService.serviceInfo.serviceName} ${honoService.serviceInfo.serviceVersion} close socket`, + destroy: async () => { + serverInstance.close() + }, + }, + honoService, + ]) } main() diff --git a/examples/temporal/src/service/ServiceEvent.enum.ts b/examples/temporal/src/service/ServiceEvent.enum.ts index c3ef38115..584eb382d 100644 --- a/examples/temporal/src/service/ServiceEvent.enum.ts +++ b/examples/temporal/src/service/ServiceEvent.enum.ts @@ -1,22 +1,22 @@ export enum ServiceEvent { - /** - * Emitted by user v1 command createUser: - * creates a new user - */ - UserCreated = 'user-created', - /** - * Emitted by email v1 command confirmEmail: - * confirms an email address - */ - EmailAddressConfirmed = 'emailAddressConfirmed', - /** - * Emitted by user v1 command register: - * registers a new user - */ - UserRegistrationStarted = 'user-registration-started', - /** - * Emitted by account v1 command createAccount: - * creates a new bank account for given user - */ - BankAccountCreated = 'bankAccountCreated', + /** + * Emitted by user v1 command createUser: + * creates a new user + */ + UserCreated = 'user-created', + /** + * Emitted by email v1 command confirmEmail: + * confirms an email address + */ + EmailAddressConfirmed = 'emailAddressConfirmed', + /** + * Emitted by user v1 command register: + * registers a new user + */ + UserRegistrationStarted = 'user-registration-started', + /** + * Emitted by account v1 command createAccount: + * creates a new bank account for given user + */ + BankAccountCreated = 'bankAccountCreated', } diff --git a/examples/temporal/src/service/account/generalAccountServiceInfo.ts b/examples/temporal/src/service/account/generalAccountServiceInfo.ts index 62fcc2f1c..11b4dd801 100644 --- a/examples/temporal/src/service/account/generalAccountServiceInfo.ts +++ b/examples/temporal/src/service/account/generalAccountServiceInfo.ts @@ -1,6 +1,6 @@ import type { ServiceInfoType } from '@purista/core' export const generalAccountServiceInfo = { - serviceName: 'Account', - serviceDescription: 'bank account domain', + serviceName: 'Account', + serviceDescription: 'bank account domain', } as const satisfies Omit diff --git a/examples/temporal/src/service/account/v1/accountV1Service.test.ts b/examples/temporal/src/service/account/v1/accountV1Service.test.ts index 91d34f8cf..4d4db26b2 100644 --- a/examples/temporal/src/service/account/v1/accountV1Service.test.ts +++ b/examples/temporal/src/service/account/v1/accountV1Service.test.ts @@ -1,7 +1,7 @@ import { accountV1Service as service } from './accountV1Service.js' describe('service account version 1', () => { - it('has valid setup', async () => { - await expect(service.testServiceSetup()).resolves.toBeTruthy() - }) + it('has valid setup', async () => { + await expect(service.testServiceSetup()).resolves.toBeTruthy() + }) }) diff --git a/examples/temporal/src/service/account/v1/accountV1Service.ts b/examples/temporal/src/service/account/v1/accountV1Service.ts index 5b1ece340..d39b53b89 100644 --- a/examples/temporal/src/service/account/v1/accountV1Service.ts +++ b/examples/temporal/src/service/account/v1/accountV1Service.ts @@ -12,5 +12,5 @@ const commandDefinitions: CommandDefinitionList = [createAccountCommandBuil const subscriptionDefinitions: SubscriptionDefinitionList = [] export const accountV1Service = accountV1ServiceBuilder - .addCommandDefinition(...commandDefinitions) - .addSubscriptionDefinition(...subscriptionDefinitions) + .addCommandDefinition(...commandDefinitions) + .addSubscriptionDefinition(...subscriptionDefinitions) diff --git a/examples/temporal/src/service/account/v1/accountV1ServiceBuilder.ts b/examples/temporal/src/service/account/v1/accountV1ServiceBuilder.ts index 0d4adc93e..7e31476c9 100644 --- a/examples/temporal/src/service/account/v1/accountV1ServiceBuilder.ts +++ b/examples/temporal/src/service/account/v1/accountV1ServiceBuilder.ts @@ -5,12 +5,12 @@ import { generalAccountServiceInfo } from '../generalAccountServiceInfo.js' import { accountServiceV1ConfigSchema } from './accountServiceConfig.js' export const accountServiceInfo = { - serviceVersion: '1', - ...generalAccountServiceInfo, + serviceVersion: '1', + ...generalAccountServiceInfo, } as const satisfies ServiceInfoType // create a service builder instance and assign service config schema and default config. -export const accountV1ServiceBuilder = new ServiceBuilder(accountServiceInfo) - .setConfigSchema(accountServiceV1ConfigSchema) - .setDefaultConfig({}) +export const accountV1ServiceBuilder = new ServiceBuilder(accountServiceInfo).setConfigSchema( + accountServiceV1ConfigSchema, +) diff --git a/examples/temporal/src/service/account/v1/command/createAccount/createAccount.test.ts b/examples/temporal/src/service/account/v1/command/createAccount/createAccount.test.ts index 045d427cb..021b9c598 100644 --- a/examples/temporal/src/service/account/v1/command/createAccount/createAccount.test.ts +++ b/examples/temporal/src/service/account/v1/command/createAccount/createAccount.test.ts @@ -6,30 +6,30 @@ import { createAccountCommandBuilder } from './createAccountCommandBuilder.js' import type { AccountV1CreateAccountInputParameter, AccountV1CreateAccountInputPayload } from './types.js' describe('service Account version 1 - command createAccount', () => { - let sandbox = createSandbox() - beforeEach(() => { - sandbox = createSandbox() - }) + let sandbox = createSandbox() + beforeEach(() => { + sandbox = createSandbox() + }) - afterEach(() => { - sandbox.restore() - }) + afterEach(() => { + sandbox.restore() + }) - test('does not throw', async () => { - const service = await accountV1Service.getInstance(getEventBridgeMock(sandbox).mock, { - logger: getLoggerMock(sandbox).mock, - }) + test('does not throw', async () => { + const service = await accountV1Service.getInstance(getEventBridgeMock(sandbox).mock, { + logger: getLoggerMock(sandbox).mock, + }) - const createAccount = safeBind(createAccountCommandBuilder.getCommandFunction(), service) + const createAccount = safeBind(createAccountCommandBuilder.getCommandFunction(), service) - const payload: AccountV1CreateAccountInputPayload = undefined + const payload: AccountV1CreateAccountInputPayload = undefined - const parameter: AccountV1CreateAccountInputParameter = {} + const parameter: AccountV1CreateAccountInputParameter = {} - const context = createAccountCommandBuilder.getCommandContextMock(payload, parameter, sandbox) + const context = createAccountCommandBuilder.getCommandContextMock({ payload, parameter, sandbox }) - const result = await createAccount(context.mock, payload, parameter) + const result = await createAccount(context.mock, payload, parameter) - expect(result).toBeUndefined() - }) + expect(result).toBeUndefined() + }) }) diff --git a/examples/temporal/src/service/account/v1/command/createAccount/createAccountCommandBuilder.ts b/examples/temporal/src/service/account/v1/command/createAccount/createAccountCommandBuilder.ts index 4561d53f3..33ee6c29c 100644 --- a/examples/temporal/src/service/account/v1/command/createAccount/createAccountCommandBuilder.ts +++ b/examples/temporal/src/service/account/v1/command/createAccount/createAccountCommandBuilder.ts @@ -1,17 +1,17 @@ import { ServiceEvent } from '../../../../ServiceEvent.enum.js' import { accountV1ServiceBuilder } from '../../accountV1ServiceBuilder.js' import { - accountV1CreateAccountInputParameterSchema, - accountV1CreateAccountInputPayloadSchema, - accountV1CreateAccountOutputPayloadSchema, + accountV1CreateAccountInputParameterSchema, + accountV1CreateAccountInputPayloadSchema, + accountV1CreateAccountOutputPayloadSchema, } from './schema.js' export const createAccountCommandBuilder = accountV1ServiceBuilder - .getCommandBuilder('createAccount', 'creates a new bank account for given user') - .setSuccessEventName(ServiceEvent.BankAccountCreated) - .addPayloadSchema(accountV1CreateAccountInputPayloadSchema) - .addParameterSchema(accountV1CreateAccountInputParameterSchema) - .addOutputSchema(accountV1CreateAccountOutputPayloadSchema) - .setCommandFunction(async function (_context, _payload, _parameter) { - // add your business logic here - }) + .getCommandBuilder('createAccount', 'creates a new bank account for given user') + .setSuccessEventName(ServiceEvent.BankAccountCreated) + .addPayloadSchema(accountV1CreateAccountInputPayloadSchema) + .addParameterSchema(accountV1CreateAccountInputParameterSchema) + .addOutputSchema(accountV1CreateAccountOutputPayloadSchema) + .setCommandFunction(async function (_context, _payload, _parameter) { + // add your business logic here + }) diff --git a/examples/temporal/src/service/account/v1/command/createAccount/schema.ts b/examples/temporal/src/service/account/v1/command/createAccount/schema.ts index 7a09effe5..42ccc1653 100644 --- a/examples/temporal/src/service/account/v1/command/createAccount/schema.ts +++ b/examples/temporal/src/service/account/v1/command/createAccount/schema.ts @@ -3,15 +3,15 @@ import { z } from 'zod' // define the input parameters export const accountV1CreateAccountInputParameterSchema = extendApi(z.object({}), { - title: 'createAccount input parameter schema', + title: 'createAccount input parameter schema', }) // define the input payload export const accountV1CreateAccountInputPayloadSchema = extendApi(z.any(), { - title: 'createAccount input payload schema', + title: 'createAccount input payload schema', }) // define the output payload export const accountV1CreateAccountOutputPayloadSchema = extendApi(z.any(), { - title: 'createAccount output payload schema', + title: 'createAccount output payload schema', }) diff --git a/examples/temporal/src/service/account/v1/command/createAccount/types.ts b/examples/temporal/src/service/account/v1/command/createAccount/types.ts index b23f1ae7e..37c42c059 100644 --- a/examples/temporal/src/service/account/v1/command/createAccount/types.ts +++ b/examples/temporal/src/service/account/v1/command/createAccount/types.ts @@ -1,9 +1,9 @@ import type { z } from 'zod' import type { - accountV1CreateAccountInputParameterSchema, - accountV1CreateAccountInputPayloadSchema, - accountV1CreateAccountOutputPayloadSchema, + accountV1CreateAccountInputParameterSchema, + accountV1CreateAccountInputPayloadSchema, + accountV1CreateAccountOutputPayloadSchema, } from './schema.js' export type AccountV1CreateAccountInputParameter = z.input diff --git a/examples/temporal/src/service/card/generalCardServiceInfo.ts b/examples/temporal/src/service/card/generalCardServiceInfo.ts index 1e9bca162..bb0b5cf16 100644 --- a/examples/temporal/src/service/card/generalCardServiceInfo.ts +++ b/examples/temporal/src/service/card/generalCardServiceInfo.ts @@ -1,6 +1,6 @@ import type { ServiceInfoType } from '@purista/core' export const generalCardServiceInfo = { - serviceName: 'Card', - serviceDescription: 'the credit card domain', + serviceName: 'Card', + serviceDescription: 'the credit card domain', } as const satisfies Omit diff --git a/examples/temporal/src/service/card/v1/cardV1Service.test.ts b/examples/temporal/src/service/card/v1/cardV1Service.test.ts index 53918d544..daaa96866 100644 --- a/examples/temporal/src/service/card/v1/cardV1Service.test.ts +++ b/examples/temporal/src/service/card/v1/cardV1Service.test.ts @@ -1,7 +1,7 @@ import { cardV1Service as service } from './cardV1Service.js' describe('service card version 1', () => { - it('has valid setup', async () => { - await expect(service.testServiceSetup()).resolves.toBeTruthy() - }) + it('has valid setup', async () => { + await expect(service.testServiceSetup()).resolves.toBeTruthy() + }) }) diff --git a/examples/temporal/src/service/card/v1/cardV1Service.ts b/examples/temporal/src/service/card/v1/cardV1Service.ts index 0d006077c..e0dff797d 100644 --- a/examples/temporal/src/service/card/v1/cardV1Service.ts +++ b/examples/temporal/src/service/card/v1/cardV1Service.ts @@ -11,5 +11,5 @@ const commandDefinitions: CommandDefinitionList = [] const subscriptionDefinitions: SubscriptionDefinitionList = [] export const cardV1Service = cardV1ServiceBuilder - .addCommandDefinition(...commandDefinitions) - .addSubscriptionDefinition(...subscriptionDefinitions) + .addCommandDefinition(...commandDefinitions) + .addSubscriptionDefinition(...subscriptionDefinitions) diff --git a/examples/temporal/src/service/card/v1/cardV1ServiceBuilder.ts b/examples/temporal/src/service/card/v1/cardV1ServiceBuilder.ts index 45557df15..ea418a301 100644 --- a/examples/temporal/src/service/card/v1/cardV1ServiceBuilder.ts +++ b/examples/temporal/src/service/card/v1/cardV1ServiceBuilder.ts @@ -5,12 +5,10 @@ import { generalCardServiceInfo } from '../generalCardServiceInfo.js' import { cardServiceV1ConfigSchema } from './cardServiceConfig.js' export const cardServiceInfo = { - serviceVersion: '1', - ...generalCardServiceInfo, + serviceVersion: '1', + ...generalCardServiceInfo, } as const satisfies ServiceInfoType // create a service builder instance and assign service config schema and default config. -export const cardV1ServiceBuilder = new ServiceBuilder(cardServiceInfo) - .setConfigSchema(cardServiceV1ConfigSchema) - .setDefaultConfig({}) +export const cardV1ServiceBuilder = new ServiceBuilder(cardServiceInfo).setConfigSchema(cardServiceV1ConfigSchema) diff --git a/examples/temporal/src/service/email/generalEmailServiceInfo.ts b/examples/temporal/src/service/email/generalEmailServiceInfo.ts index 37f999976..c8e7833d2 100644 --- a/examples/temporal/src/service/email/generalEmailServiceInfo.ts +++ b/examples/temporal/src/service/email/generalEmailServiceInfo.ts @@ -1,6 +1,6 @@ import type { ServiceInfoType } from '@purista/core' export const generalEmailServiceInfo = { - serviceName: 'Email', - serviceDescription: 'the emmail domain', + serviceName: 'Email', + serviceDescription: 'the emmail domain', } as const satisfies Omit diff --git a/examples/temporal/src/service/email/v1/command/confirmEmail/confirmEmail.test.ts b/examples/temporal/src/service/email/v1/command/confirmEmail/confirmEmail.test.ts index 2cd503afd..1c95d4fae 100644 --- a/examples/temporal/src/service/email/v1/command/confirmEmail/confirmEmail.test.ts +++ b/examples/temporal/src/service/email/v1/command/confirmEmail/confirmEmail.test.ts @@ -6,53 +6,53 @@ import { emailV1Service } from '../../emailV1Service.js' import { confirmEmailCommandBuilder } from './confirmEmailCommandBuilder.js' import type { EmailV1ConfirmEmailInputParameter, EmailV1ConfirmEmailInputPayload } from './types.js' -vi.mock('@temporalio/client', async (importOriginal) => { - return { - ...((await importOriginal()) as {}), - Connection: { - connect: () => {}, - }, - Client: class ClientMock { - public workflow = { - getHandle: () => { - return { - signal: async () => {}, - } - }, - } - }, - } +vi.mock('@temporalio/client', async importOriginal => { + return { + ...(await importOriginal>()), + Connection: { + connect: () => {}, + }, + Client: class ClientMock { + public workflow = { + getHandle: () => { + return { + signal: async () => {}, + } + }, + } + }, + } }) describe('service Email version 1 - command confirmEmail', () => { - let sandbox = createSandbox() - beforeEach(() => { - sandbox = createSandbox() - }) + let sandbox = createSandbox() + beforeEach(() => { + sandbox = createSandbox() + }) - afterEach(() => { - sandbox.restore() - }) + afterEach(() => { + sandbox.restore() + }) - test('does not throw', async () => { - const service = await emailV1Service.getInstance(getEventBridgeMock(sandbox).mock, { - logger: getLoggerMock(sandbox).mock, - }) + test('does not throw', async () => { + const service = await emailV1Service.getInstance(getEventBridgeMock(sandbox).mock, { + logger: getLoggerMock(sandbox).mock, + }) - const confirmEmail = safeBind(confirmEmailCommandBuilder.getCommandFunction(), service) + const confirmEmail = safeBind(confirmEmailCommandBuilder.getCommandFunction(), service) - const payload: EmailV1ConfirmEmailInputPayload = { - email: 'john@example.com', - } + const payload: EmailV1ConfirmEmailInputPayload = { + email: 'john@example.com', + } - const parameter: EmailV1ConfirmEmailInputParameter = {} + const parameter: EmailV1ConfirmEmailInputParameter = {} - const context = confirmEmailCommandBuilder.getCommandContextMock(payload, parameter, sandbox) + const context = confirmEmailCommandBuilder.getCommandContextMock({ payload, parameter, sandbox }) - context.stubs.getState.resolves({ 'john@example.com': 'john@example.com' }) + context.stubs.getState.resolves({ 'john@example.com': 'john@example.com' }) - const result = await confirmEmail(context.mock, payload, parameter) + const result = await confirmEmail(context.mock, payload, parameter) - expect(result).toBeUndefined() - }) + expect(result).toBeUndefined() + }) }) diff --git a/examples/temporal/src/service/email/v1/command/confirmEmail/confirmEmailCommandBuilder.ts b/examples/temporal/src/service/email/v1/command/confirmEmail/confirmEmailCommandBuilder.ts index 9b437cbb7..7328dba12 100644 --- a/examples/temporal/src/service/email/v1/command/confirmEmail/confirmEmailCommandBuilder.ts +++ b/examples/temporal/src/service/email/v1/command/confirmEmail/confirmEmailCommandBuilder.ts @@ -5,41 +5,41 @@ import { OpenTelemetryWorkflowClientInterceptor } from '@temporalio/interceptors import { ServiceEvent } from '../../../../ServiceEvent.enum.js' import { emailV1ServiceBuilder } from '../../emailV1ServiceBuilder.js' import { - emailV1ConfirmEmailInputParameterSchema, - emailV1ConfirmEmailInputPayloadSchema, - emailV1ConfirmEmailOutputPayloadSchema, + emailV1ConfirmEmailInputParameterSchema, + emailV1ConfirmEmailInputPayloadSchema, + emailV1ConfirmEmailOutputPayloadSchema, } from './schema.js' export const confirmEmailCommandBuilder = emailV1ServiceBuilder - .getCommandBuilder('confirmEmail', 'confirms an email address') - .setSuccessEventName(ServiceEvent.EmailAddressConfirmed) - .addPayloadSchema(emailV1ConfirmEmailInputPayloadSchema) - .addParameterSchema(emailV1ConfirmEmailInputParameterSchema) - .addOutputSchema(emailV1ConfirmEmailOutputPayloadSchema) - .exposeAsHttpEndpoint('POST', 'verify') - .disableHttpSecurity() - .setCommandFunction(async function ({ states }, payload, _parameter) { - const exist = await states.getState(payload.email) - if (!exist[payload.email]) { - throw new HandledError(StatusCode.BadRequest, 'Unknown email') - } + .getCommandBuilder('confirmEmail', 'confirms an email address') + .setSuccessEventName(ServiceEvent.EmailAddressConfirmed) + .addPayloadSchema(emailV1ConfirmEmailInputPayloadSchema) + .addParameterSchema(emailV1ConfirmEmailInputParameterSchema) + .addOutputSchema(emailV1ConfirmEmailOutputPayloadSchema) + .exposeAsHttpEndpoint('POST', 'verify') + .disableHttpSecurity() + .setCommandFunction(async function ({ states }, payload, _parameter) { + const exist = await states.getState(payload.email) + if (!exist[payload.email]) { + throw new HandledError(StatusCode.BadRequest, 'Unknown email') + } - const connection = await Connection.connect({ address: 'localhost:7233' }) + const connection = await Connection.connect({ address: 'localhost:7233' }) - const client = new Client({ - connection, - interceptors: { - workflow: [new OpenTelemetryWorkflowClientInterceptor({ tracer: this.getTracer() })], - }, - }) + const client = new Client({ + connection, + interceptors: { + workflow: [new OpenTelemetryWorkflowClientInterceptor({ tracer: this.getTracer() })], + }, + }) - try { - await client.workflow.getHandle(`onboarding-${payload.email}`).signal('signal-email-verified') - } catch (error) { - const err = error as Error - if (err.name === 'WorkflowNotFoundError') { - throw new HandledError(StatusCode.BadRequest, 'Invalid email') - } - throw UnhandledError.fromError(err) - } - }) + try { + await client.workflow.getHandle(`onboarding-${payload.email}`).signal('signal-email-verified') + } catch (error) { + const err = error as Error + if (err.name === 'WorkflowNotFoundError') { + throw new HandledError(StatusCode.BadRequest, 'Invalid email') + } + throw UnhandledError.fromError(err) + } + }) diff --git a/examples/temporal/src/service/email/v1/command/confirmEmail/schema.ts b/examples/temporal/src/service/email/v1/command/confirmEmail/schema.ts index ba8ede108..24d836365 100644 --- a/examples/temporal/src/service/email/v1/command/confirmEmail/schema.ts +++ b/examples/temporal/src/service/email/v1/command/confirmEmail/schema.ts @@ -3,18 +3,18 @@ import { z } from 'zod' // define the input parameters export const emailV1ConfirmEmailInputParameterSchema = extendApi(z.object({}), { - title: 'confirmEmail input parameter schema', + title: 'confirmEmail input parameter schema', }) // define the input payload export const emailV1ConfirmEmailInputPayloadSchema = extendApi( - z.object({ - email: extendApi(z.string().email().toLowerCase(), { title: 'The users name', example: 'john_doe@example.com' }), - }), - { title: 'confirmEmail input payload schema' }, + z.object({ + email: extendApi(z.string().email().toLowerCase(), { title: 'The users name', example: 'john_doe@example.com' }), + }), + { title: 'confirmEmail input payload schema' }, ) // define the output payload export const emailV1ConfirmEmailOutputPayloadSchema = extendApi(z.any(), { - title: 'confirmEmail output payload schema', + title: 'confirmEmail output payload schema', }) diff --git a/examples/temporal/src/service/email/v1/command/confirmEmail/types.ts b/examples/temporal/src/service/email/v1/command/confirmEmail/types.ts index 59a131630..fd53baeeb 100644 --- a/examples/temporal/src/service/email/v1/command/confirmEmail/types.ts +++ b/examples/temporal/src/service/email/v1/command/confirmEmail/types.ts @@ -1,9 +1,9 @@ import type { z } from 'zod' import type { - emailV1ConfirmEmailInputParameterSchema, - emailV1ConfirmEmailInputPayloadSchema, - emailV1ConfirmEmailOutputPayloadSchema, + emailV1ConfirmEmailInputParameterSchema, + emailV1ConfirmEmailInputPayloadSchema, + emailV1ConfirmEmailOutputPayloadSchema, } from './schema.js' export type EmailV1ConfirmEmailInputParameter = z.input diff --git a/examples/temporal/src/service/email/v1/command/sendVerificationEmail/schema.ts b/examples/temporal/src/service/email/v1/command/sendVerificationEmail/schema.ts index a6938bbea..5a8a07b79 100644 --- a/examples/temporal/src/service/email/v1/command/sendVerificationEmail/schema.ts +++ b/examples/temporal/src/service/email/v1/command/sendVerificationEmail/schema.ts @@ -3,20 +3,20 @@ import { z } from 'zod' // define the input parameters export const emailV1SendVerificationEmailInputParameterSchema = extendApi(z.object({}), { - title: 'sendVerificationEmail input parameter schema', + title: 'sendVerificationEmail input parameter schema', }) // define the input payload export const emailV1SendVerificationEmailInputPayloadSchema = extendApi( - z.object({ - email: extendApi(z.string().email().toLowerCase(), { title: 'The users name', example: 'john_doe@example.com' }), - }), - { - title: 'sendVerificationEmail input payload schema', - }, + z.object({ + email: extendApi(z.string().email().toLowerCase(), { title: 'The users name', example: 'john_doe@example.com' }), + }), + { + title: 'sendVerificationEmail input payload schema', + }, ) // define the output payload export const emailV1SendVerificationEmailOutputPayloadSchema = extendApi(z.any(), { - title: 'sendVerificationEmail output payload schema', + title: 'sendVerificationEmail output payload schema', }) diff --git a/examples/temporal/src/service/email/v1/command/sendVerificationEmail/sendVerificationEmail.test.ts b/examples/temporal/src/service/email/v1/command/sendVerificationEmail/sendVerificationEmail.test.ts index 05c89bdae..17c3df92a 100644 --- a/examples/temporal/src/service/email/v1/command/sendVerificationEmail/sendVerificationEmail.test.ts +++ b/examples/temporal/src/service/email/v1/command/sendVerificationEmail/sendVerificationEmail.test.ts @@ -6,34 +6,34 @@ import { sendVerificationEmailCommandBuilder } from './sendVerificationEmailComm import type { EmailV1SendVerificationEmailInputParameter, EmailV1SendVerificationEmailInputPayload } from './types.js' describe('service Email version 1 - command sendVerificationEmail', () => { - let sandbox = createSandbox() - beforeEach(() => { - sandbox = createSandbox() - }) + let sandbox = createSandbox() + beforeEach(() => { + sandbox = createSandbox() + }) - afterEach(() => { - sandbox.restore() - }) + afterEach(() => { + sandbox.restore() + }) - test('does not throw', async () => { - const service = await emailV1Service.getInstance(getEventBridgeMock(sandbox).mock, { - logger: getLoggerMock(sandbox).mock, - }) + test('does not throw', async () => { + const service = await emailV1Service.getInstance(getEventBridgeMock(sandbox).mock, { + logger: getLoggerMock(sandbox).mock, + }) - const sendVerificationEmail = safeBind(sendVerificationEmailCommandBuilder.getCommandFunction(), service) + const sendVerificationEmail = safeBind(sendVerificationEmailCommandBuilder.getCommandFunction(), service) - const payload: EmailV1SendVerificationEmailInputPayload = { - email: 'john@example.com', - } + const payload: EmailV1SendVerificationEmailInputPayload = { + email: 'john@example.com', + } - const parameter: EmailV1SendVerificationEmailInputParameter = {} + const parameter: EmailV1SendVerificationEmailInputParameter = {} - const context = sendVerificationEmailCommandBuilder.getCommandContextMock(payload, parameter, sandbox) + const context = sendVerificationEmailCommandBuilder.getCommandContextMock({ payload, parameter, sandbox }) - context.stubs.setState.resolves() + context.stubs.setState.resolves() - const result = await sendVerificationEmail(context.mock, payload, parameter) + const result = await sendVerificationEmail(context.mock, payload, parameter) - expect(result).toBeUndefined() - }) + expect(result).toBeUndefined() + }) }) diff --git a/examples/temporal/src/service/email/v1/command/sendVerificationEmail/sendVerificationEmailCommandBuilder.ts b/examples/temporal/src/service/email/v1/command/sendVerificationEmail/sendVerificationEmailCommandBuilder.ts index 74ac4940f..d03e3c8e0 100644 --- a/examples/temporal/src/service/email/v1/command/sendVerificationEmail/sendVerificationEmailCommandBuilder.ts +++ b/examples/temporal/src/service/email/v1/command/sendVerificationEmail/sendVerificationEmailCommandBuilder.ts @@ -1,17 +1,17 @@ import { emailV1ServiceBuilder } from '../../emailV1ServiceBuilder.js' import { - emailV1SendVerificationEmailInputParameterSchema, - emailV1SendVerificationEmailInputPayloadSchema, - emailV1SendVerificationEmailOutputPayloadSchema, + emailV1SendVerificationEmailInputParameterSchema, + emailV1SendVerificationEmailInputPayloadSchema, + emailV1SendVerificationEmailOutputPayloadSchema, } from './schema.js' export const sendVerificationEmailCommandBuilder = emailV1ServiceBuilder - .getCommandBuilder('sendVerificationEmail', 'sends a verification email') - .addPayloadSchema(emailV1SendVerificationEmailInputPayloadSchema) - .addParameterSchema(emailV1SendVerificationEmailInputParameterSchema) - .addOutputSchema(emailV1SendVerificationEmailOutputPayloadSchema) - .setCommandFunction(async function ({ logger, states }, payload, _parameter) { - // add your business logic here - logger.info({ payload }, 'sendVerificationEmail called') - await states.setState(payload.email, payload) - }) + .getCommandBuilder('sendVerificationEmail', 'sends a verification email') + .addPayloadSchema(emailV1SendVerificationEmailInputPayloadSchema) + .addParameterSchema(emailV1SendVerificationEmailInputParameterSchema) + .addOutputSchema(emailV1SendVerificationEmailOutputPayloadSchema) + .setCommandFunction(async function ({ logger, states }, payload, _parameter) { + // add your business logic here + logger.info({ payload }, 'sendVerificationEmail called') + await states.setState(payload.email, payload) + }) diff --git a/examples/temporal/src/service/email/v1/command/sendVerificationEmail/types.ts b/examples/temporal/src/service/email/v1/command/sendVerificationEmail/types.ts index 9a1aa2a67..1cee40f5b 100644 --- a/examples/temporal/src/service/email/v1/command/sendVerificationEmail/types.ts +++ b/examples/temporal/src/service/email/v1/command/sendVerificationEmail/types.ts @@ -1,13 +1,13 @@ import type { z } from 'zod' import type { - emailV1SendVerificationEmailInputParameterSchema, - emailV1SendVerificationEmailInputPayloadSchema, - emailV1SendVerificationEmailOutputPayloadSchema, + emailV1SendVerificationEmailInputParameterSchema, + emailV1SendVerificationEmailInputPayloadSchema, + emailV1SendVerificationEmailOutputPayloadSchema, } from './schema.js' export type EmailV1SendVerificationEmailInputParameter = z.input< - typeof emailV1SendVerificationEmailInputParameterSchema + typeof emailV1SendVerificationEmailInputParameterSchema > export type EmailV1SendVerificationEmailInputPayload = z.input diff --git a/examples/temporal/src/service/email/v1/emailV1Service.test.ts b/examples/temporal/src/service/email/v1/emailV1Service.test.ts index f98f1e94b..d0fea190d 100644 --- a/examples/temporal/src/service/email/v1/emailV1Service.test.ts +++ b/examples/temporal/src/service/email/v1/emailV1Service.test.ts @@ -1,7 +1,7 @@ import { emailV1Service as service } from './emailV1Service.js' describe('service email version 1', () => { - it('has valid setup', async () => { - await expect(service.testServiceSetup()).resolves.toBeTruthy() - }) + it('has valid setup', async () => { + await expect(service.testServiceSetup()).resolves.toBeTruthy() + }) }) diff --git a/examples/temporal/src/service/email/v1/emailV1Service.ts b/examples/temporal/src/service/email/v1/emailV1Service.ts index 62a508908..fcbefacdc 100644 --- a/examples/temporal/src/service/email/v1/emailV1Service.ts +++ b/examples/temporal/src/service/email/v1/emailV1Service.ts @@ -9,12 +9,12 @@ import { emailV1ServiceBuilder } from './emailV1ServiceBuilder.js' // other service config should be done in ./emailServiceBuilder.ts file const commandDefinitions: CommandDefinitionList = [ - confirmEmailCommandBuilder.getDefinition(), - sendVerificationEmailCommandBuilder.getDefinition(), + confirmEmailCommandBuilder.getDefinition(), + sendVerificationEmailCommandBuilder.getDefinition(), ] const subscriptionDefinitions: SubscriptionDefinitionList = [] export const emailV1Service = emailV1ServiceBuilder - .addCommandDefinition(...commandDefinitions) - .addSubscriptionDefinition(...subscriptionDefinitions) + .addCommandDefinition(...commandDefinitions) + .addSubscriptionDefinition(...subscriptionDefinitions) diff --git a/examples/temporal/src/service/email/v1/emailV1ServiceBuilder.ts b/examples/temporal/src/service/email/v1/emailV1ServiceBuilder.ts index ab2fd3b9a..926b0389e 100644 --- a/examples/temporal/src/service/email/v1/emailV1ServiceBuilder.ts +++ b/examples/temporal/src/service/email/v1/emailV1ServiceBuilder.ts @@ -5,12 +5,10 @@ import { generalEmailServiceInfo } from '../generalEmailServiceInfo.js' import { emailServiceV1ConfigSchema } from './emailServiceConfig.js' export const emailServiceInfo = { - serviceVersion: '1', - ...generalEmailServiceInfo, + serviceVersion: '1', + ...generalEmailServiceInfo, } as const satisfies ServiceInfoType // create a service builder instance and assign service config schema and default config. -export const emailV1ServiceBuilder = new ServiceBuilder(emailServiceInfo) - .setConfigSchema(emailServiceV1ConfigSchema) - .setDefaultConfig({}) +export const emailV1ServiceBuilder = new ServiceBuilder(emailServiceInfo).setConfigSchema(emailServiceV1ConfigSchema) diff --git a/examples/temporal/src/service/user/generalUserServiceInfo.ts b/examples/temporal/src/service/user/generalUserServiceInfo.ts index 7107f9182..bff908ff6 100644 --- a/examples/temporal/src/service/user/generalUserServiceInfo.ts +++ b/examples/temporal/src/service/user/generalUserServiceInfo.ts @@ -1,6 +1,6 @@ import type { ServiceInfoType } from '@purista/core' export const generalUserServiceInfo = { - serviceName: 'User', - serviceDescription: 'the user domain service', + serviceName: 'User', + serviceDescription: 'the user domain service', } as const satisfies Omit diff --git a/examples/temporal/src/service/user/v1/command/createUser/createUser.test.ts b/examples/temporal/src/service/user/v1/command/createUser/createUser.test.ts index 8a653be1b..9ef73232d 100644 --- a/examples/temporal/src/service/user/v1/command/createUser/createUser.test.ts +++ b/examples/temporal/src/service/user/v1/command/createUser/createUser.test.ts @@ -6,33 +6,40 @@ import { createUserCommandBuilder } from './createUserCommandBuilder.js' import type { UserV1CreateUserInputParameter, UserV1CreateUserInputPayload } from './types.js' describe('service User version 1 - command createUser', () => { - let sandbox = createSandbox() - beforeEach(() => { - sandbox = createSandbox() - }) - - afterEach(() => { - sandbox.restore() - }) - - test('does not throw', async () => { - const service = await userV1Service.getInstance(getEventBridgeMock(sandbox).mock, { - logger: getLoggerMock(sandbox).mock, - }) - - const createUser = safeBind(createUserCommandBuilder.getCommandFunction(), service) - - const payload: UserV1CreateUserInputPayload = { - name: 'John Doe', - email: 'john@example.com', - } - - const parameter: UserV1CreateUserInputParameter = {} - - const context = createUserCommandBuilder.getCommandContextMock(payload, parameter, sandbox) - - const result = await createUser(context.mock, payload, parameter) - - expect(result.userId).toBeDefined() - }) + let sandbox = createSandbox() + beforeEach(() => { + sandbox = createSandbox() + }) + + afterEach(() => { + sandbox.restore() + }) + + test('does not throw', async () => { + const service = await userV1Service.getInstance(getEventBridgeMock(sandbox).mock, { + logger: getLoggerMock(sandbox).mock, + serviceConfig: { + taskQueue: 'example.com', + namespace: 'example.com', + connect: { + address: 'example.com', + }, + }, + }) + + const createUser = safeBind(createUserCommandBuilder.getCommandFunction(), service) + + const payload: UserV1CreateUserInputPayload = { + name: 'John Doe', + email: 'john@example.com', + } + + const parameter: UserV1CreateUserInputParameter = {} + + const context = createUserCommandBuilder.getCommandContextMock({ payload, parameter, sandbox }) + + const result = await createUser(context.mock, payload, parameter) + + expect(result.userId).toBeDefined() + }) }) diff --git a/examples/temporal/src/service/user/v1/command/createUser/createUserCommandBuilder.ts b/examples/temporal/src/service/user/v1/command/createUser/createUserCommandBuilder.ts index 3f144deb5..98ae04149 100644 --- a/examples/temporal/src/service/user/v1/command/createUser/createUserCommandBuilder.ts +++ b/examples/temporal/src/service/user/v1/command/createUser/createUserCommandBuilder.ts @@ -3,21 +3,21 @@ import { randomUUID } from 'node:crypto' import { ServiceEvent } from '../../../../ServiceEvent.enum.js' import { userV1ServiceBuilder } from '../../userV1ServiceBuilder.js' import { - userV1CreateUserInputParameterSchema, - userV1CreateUserInputPayloadSchema, - userV1CreateUserOutputPayloadSchema, + userV1CreateUserInputParameterSchema, + userV1CreateUserInputPayloadSchema, + userV1CreateUserOutputPayloadSchema, } from './schema.js' export const createUserCommandBuilder = userV1ServiceBuilder - .getCommandBuilder('createUser', 'creates a new user') - .setSuccessEventName(ServiceEvent.UserCreated) - .addPayloadSchema(userV1CreateUserInputPayloadSchema) - .addParameterSchema(userV1CreateUserInputParameterSchema) - .addOutputSchema(userV1CreateUserOutputPayloadSchema) - .setCommandFunction(async function ({ logger }, payload, _parameter) { - logger.info({ payload }, 'create new user called') + .getCommandBuilder('createUser', 'creates a new user') + .setSuccessEventName(ServiceEvent.UserCreated) + .addPayloadSchema(userV1CreateUserInputPayloadSchema) + .addParameterSchema(userV1CreateUserInputParameterSchema) + .addOutputSchema(userV1CreateUserOutputPayloadSchema) + .setCommandFunction(async function ({ logger }, payload, _parameter) { + logger.info({ payload }, 'create new user called') - return { - userId: randomUUID(), - } - }) + return { + userId: randomUUID(), + } + }) diff --git a/examples/temporal/src/service/user/v1/command/createUser/schema.ts b/examples/temporal/src/service/user/v1/command/createUser/schema.ts index 2d2b9ee87..aa08e5a55 100644 --- a/examples/temporal/src/service/user/v1/command/createUser/schema.ts +++ b/examples/temporal/src/service/user/v1/command/createUser/schema.ts @@ -3,22 +3,22 @@ import { z } from 'zod' // define the input parameters export const userV1CreateUserInputParameterSchema = extendApi(z.object({}), { - title: 'createUser input parameter schema', + title: 'createUser input parameter schema', }) // define the input payload export const userV1CreateUserInputPayloadSchema = extendApi( - z.object({ - name: extendApi(z.string(), { title: 'The users name', example: 'John Doe' }), - email: extendApi(z.string().email().toLowerCase(), { title: 'The users name', example: 'john_doe@example.com' }), - }), - { title: 'createUser input payload schema' }, + z.object({ + name: extendApi(z.string(), { title: 'The users name', example: 'John Doe' }), + email: extendApi(z.string().email().toLowerCase(), { title: 'The users name', example: 'john_doe@example.com' }), + }), + { title: 'createUser input payload schema' }, ) // define the output payload export const userV1CreateUserOutputPayloadSchema = extendApi( - z.object({ - userId: extendApi(z.string().uuid(), { title: 'the user id', example: 'adda98ce-0f21-45ca-bee8-326126c9943a' }), - }), - { title: 'createUser output payload schema' }, + z.object({ + userId: extendApi(z.string().uuid(), { title: 'the user id', example: 'adda98ce-0f21-45ca-bee8-326126c9943a' }), + }), + { title: 'createUser output payload schema' }, ) diff --git a/examples/temporal/src/service/user/v1/command/createUser/types.ts b/examples/temporal/src/service/user/v1/command/createUser/types.ts index 9669e4d84..ffc7fc856 100644 --- a/examples/temporal/src/service/user/v1/command/createUser/types.ts +++ b/examples/temporal/src/service/user/v1/command/createUser/types.ts @@ -1,9 +1,9 @@ import type { z } from 'zod' import type { - userV1CreateUserInputParameterSchema, - userV1CreateUserInputPayloadSchema, - userV1CreateUserOutputPayloadSchema, + userV1CreateUserInputParameterSchema, + userV1CreateUserInputPayloadSchema, + userV1CreateUserOutputPayloadSchema, } from './schema.js' export type UserV1CreateUserInputParameter = z.input diff --git a/examples/temporal/src/service/user/v1/command/register/register.test.ts b/examples/temporal/src/service/user/v1/command/register/register.test.ts index 989d078f2..3c8a13aed 100644 --- a/examples/temporal/src/service/user/v1/command/register/register.test.ts +++ b/examples/temporal/src/service/user/v1/command/register/register.test.ts @@ -6,53 +6,60 @@ import { userV1Service } from '../../userV1Service.js' import { registerCommandBuilder } from './registerCommandBuilder.js' import type { UserV1RegisterInputParameter, UserV1RegisterInputPayload } from './types.js' -vi.mock('@temporalio/client', async (importOriginal) => { - return { - ...((await importOriginal()) as {}), - Connection: { - connect: () => {}, - }, - Client: class ClientMock { - public workflow = { - start: async () => ({ workflowId: 'the_workflowId' }), - } - }, - } +vi.mock('@temporalio/client', async importOriginal => { + return { + ...(await importOriginal>()), + Connection: { + connect: () => {}, + }, + Client: class ClientMock { + public workflow = { + start: async () => ({ workflowId: 'the_workflowId' }), + } + }, + } }) describe('service User version 1 - command register', () => { - let sandbox = createSandbox() - beforeEach(() => { - sandbox = createSandbox() - }) + let sandbox = createSandbox() + beforeEach(() => { + sandbox = createSandbox() + }) - afterEach(() => { - sandbox.restore() - }) + afterEach(() => { + sandbox.restore() + }) - test('does not throw', async () => { - const service = await userV1Service.getInstance(getEventBridgeMock(sandbox).mock, { - logger: getLoggerMock(sandbox).mock, - }) + test('does not throw', async () => { + const service = await userV1Service.getInstance(getEventBridgeMock(sandbox).mock, { + logger: getLoggerMock(sandbox).mock, + serviceConfig: { + taskQueue: 'example', + namespace: 'example', + connect: { + address: 'example.com', + }, + }, + }) - const register = safeBind(registerCommandBuilder.getCommandFunction(), service) + const register = safeBind(registerCommandBuilder.getCommandFunction(), service) - const payload: UserV1RegisterInputPayload = { - name: 'John Doe', - email: 'john@example.com', - } + const payload: UserV1RegisterInputPayload = { + name: 'John Doe', + email: 'john@example.com', + } - const parameter: UserV1RegisterInputParameter = {} + const parameter: UserV1RegisterInputParameter = {} - const context = registerCommandBuilder.getCommandContextMock(payload, parameter, sandbox) + const context = registerCommandBuilder.getCommandContextMock({ payload, parameter, sandbox }) - context.stubs.getState.resolves({}) - context.stubs.setState.resolves() + context.stubs.getState.resolves({}) + context.stubs.setState.resolves() - const result = await register(context.mock, payload, parameter) + const result = await register(context.mock, payload, parameter) - expect(result).toStrictEqual({ - workflowId: 'the_workflowId', - }) - }) + expect(result).toStrictEqual({ + workflowId: 'the_workflowId', + }) + }) }) diff --git a/examples/temporal/src/service/user/v1/command/register/registerCommandBuilder.ts b/examples/temporal/src/service/user/v1/command/register/registerCommandBuilder.ts index 51d643b68..c0144d958 100644 --- a/examples/temporal/src/service/user/v1/command/register/registerCommandBuilder.ts +++ b/examples/temporal/src/service/user/v1/command/register/registerCommandBuilder.ts @@ -5,47 +5,47 @@ import { OpenTelemetryWorkflowClientInterceptor } from '@temporalio/interceptors import { ServiceEvent } from '../../../../ServiceEvent.enum.js' import { userV1ServiceBuilder } from '../../userV1ServiceBuilder.js' import { - userV1RegisterInputParameterSchema, - userV1RegisterInputPayloadSchema, - userV1RegisterOutputPayloadSchema, + userV1RegisterInputParameterSchema, + userV1RegisterInputPayloadSchema, + userV1RegisterOutputPayloadSchema, } from './schema.js' export const registerCommandBuilder = userV1ServiceBuilder - .getCommandBuilder('register', 'registers a new user') - .setSuccessEventName(ServiceEvent.UserRegistrationStarted) - .addPayloadSchema(userV1RegisterInputPayloadSchema) - .addParameterSchema(userV1RegisterInputParameterSchema) - .addOutputSchema(userV1RegisterOutputPayloadSchema) - .exposeAsHttpEndpoint('POST', 'register') - .disableHttpSecurity() - .setCommandFunction(async function ({ logger, states }, payload, _parameter) { - const existing = await states.getState(payload.email) - - if (existing[payload.email]) { - throw new HandledError(StatusCode.BadRequest, 'Email already registered') - } - - const connection = await Connection.connect(this.config.connect) - - const client = new Client({ - connection, - namespace: this.config.namespace, - interceptors: { - workflow: [new OpenTelemetryWorkflowClientInterceptor({ tracer: this.getTracer() })], - }, - }) - - await states.setState(payload.email, payload) - - const handle = await client.workflow.start('onboardingWorkflow', { - taskQueue: this.config.taskQueue, - args: [payload], - // in practice, use a meaningful business ID, like customerId or transactionId - workflowId: `onboarding-${payload.email}`, - }) - logger.info(`Started workflow ${handle.workflowId}`) - - return { - workflowId: handle.workflowId, - } - }) + .getCommandBuilder('register', 'registers a new user') + .setSuccessEventName(ServiceEvent.UserRegistrationStarted) + .addPayloadSchema(userV1RegisterInputPayloadSchema) + .addParameterSchema(userV1RegisterInputParameterSchema) + .addOutputSchema(userV1RegisterOutputPayloadSchema) + .exposeAsHttpEndpoint('POST', 'register') + .disableHttpSecurity() + .setCommandFunction(async function ({ logger, states }, payload, _parameter) { + const existing = await states.getState(payload.email) + + if (existing[payload.email]) { + throw new HandledError(StatusCode.BadRequest, 'Email already registered') + } + + const connection = await Connection.connect(this.config.connect) + + const client = new Client({ + connection, + namespace: this.config.namespace, + interceptors: { + workflow: [new OpenTelemetryWorkflowClientInterceptor({ tracer: this.getTracer() })], + }, + }) + + await states.setState(payload.email, payload) + + const handle = await client.workflow.start('onboardingWorkflow', { + taskQueue: this.config.taskQueue, + args: [payload], + // in practice, use a meaningful business ID, like customerId or transactionId + workflowId: `onboarding-${payload.email}`, + }) + logger.info(`Started workflow ${handle.workflowId}`) + + return { + workflowId: handle.workflowId, + } + }) diff --git a/examples/temporal/src/service/user/v1/command/register/schema.ts b/examples/temporal/src/service/user/v1/command/register/schema.ts index aba64de61..81e4df0a9 100644 --- a/examples/temporal/src/service/user/v1/command/register/schema.ts +++ b/examples/temporal/src/service/user/v1/command/register/schema.ts @@ -6,17 +6,17 @@ export const userV1RegisterInputParameterSchema = extendApi(z.object({}), { titl // define the input payload export const userV1RegisterInputPayloadSchema = extendApi( - z.object({ - name: extendApi(z.string(), { title: 'The users name', example: 'John Doe' }), - email: extendApi(z.string().email().toLowerCase(), { title: 'The users name', example: 'john_doe@example.com' }), - }), - { title: 'register input payload schema' }, + z.object({ + name: extendApi(z.string(), { title: 'The users name', example: 'John Doe' }), + email: extendApi(z.string().email().toLowerCase(), { title: 'The users name', example: 'john_doe@example.com' }), + }), + { title: 'register input payload schema' }, ) // define the output payload export const userV1RegisterOutputPayloadSchema = extendApi( - z.object({ - workflowId: extendApi(z.string(), { title: 'the workflow id', example: 'some_random_id' }), - }), - { title: 'register output payload schema' }, + z.object({ + workflowId: extendApi(z.string(), { title: 'the workflow id', example: 'some_random_id' }), + }), + { title: 'register output payload schema' }, ) diff --git a/examples/temporal/src/service/user/v1/command/register/types.ts b/examples/temporal/src/service/user/v1/command/register/types.ts index 8aaa69d8a..a313e8a44 100644 --- a/examples/temporal/src/service/user/v1/command/register/types.ts +++ b/examples/temporal/src/service/user/v1/command/register/types.ts @@ -1,9 +1,9 @@ import type { z } from 'zod' import type { - userV1RegisterInputParameterSchema, - userV1RegisterInputPayloadSchema, - userV1RegisterOutputPayloadSchema, + userV1RegisterInputParameterSchema, + userV1RegisterInputPayloadSchema, + userV1RegisterOutputPayloadSchema, } from './schema.js' export type UserV1RegisterInputParameter = z.input diff --git a/examples/temporal/src/service/user/v1/userServiceConfig.ts b/examples/temporal/src/service/user/v1/userServiceConfig.ts index c6d22296d..d47dda670 100644 --- a/examples/temporal/src/service/user/v1/userServiceConfig.ts +++ b/examples/temporal/src/service/user/v1/userServiceConfig.ts @@ -3,11 +3,11 @@ import { z } from 'zod' // define the service config schema and the default service configuration export const userServiceV1ConfigSchema = z.object({ - taskQueue: z.string(), - namespace: z.string(), - connect: z.object({ - address: z.string(), - }), + taskQueue: z.string(), + namespace: z.string(), + connect: z.object({ + address: z.string(), + }), }) export type UserServiceV1Config = z.input diff --git a/examples/temporal/src/service/user/v1/userV1Service.test.ts b/examples/temporal/src/service/user/v1/userV1Service.test.ts index 66238b7fc..8ea8f3565 100644 --- a/examples/temporal/src/service/user/v1/userV1Service.test.ts +++ b/examples/temporal/src/service/user/v1/userV1Service.test.ts @@ -1,7 +1,7 @@ import { userV1Service as service } from './userV1Service.js' describe('service user version 1', () => { - it('has valid setup', async () => { - await expect(service.testServiceSetup()).resolves.toBeTruthy() - }) + it('has valid setup', async () => { + await expect(service.testServiceSetup()).resolves.toBeTruthy() + }) }) diff --git a/examples/temporal/src/service/user/v1/userV1Service.ts b/examples/temporal/src/service/user/v1/userV1Service.ts index d244a4cd1..23d7c30ca 100644 --- a/examples/temporal/src/service/user/v1/userV1Service.ts +++ b/examples/temporal/src/service/user/v1/userV1Service.ts @@ -9,12 +9,12 @@ import { userV1ServiceBuilder } from './userV1ServiceBuilder.js' // other service config should be done in ./userServiceBuilder.ts file const commandDefinitions: CommandDefinitionList = [ - createUserCommandBuilder.getDefinition(), - registerCommandBuilder.getDefinition(), + createUserCommandBuilder.getDefinition(), + registerCommandBuilder.getDefinition(), ] const subscriptionDefinitions: SubscriptionDefinitionList = [] export const userV1Service = userV1ServiceBuilder - .addCommandDefinition(...commandDefinitions) - .addSubscriptionDefinition(...subscriptionDefinitions) + .addCommandDefinition(...commandDefinitions) + .addSubscriptionDefinition(...subscriptionDefinitions) diff --git a/examples/temporal/src/service/user/v1/userV1ServiceBuilder.ts b/examples/temporal/src/service/user/v1/userV1ServiceBuilder.ts index b047cf552..57a76d32d 100644 --- a/examples/temporal/src/service/user/v1/userV1ServiceBuilder.ts +++ b/examples/temporal/src/service/user/v1/userV1ServiceBuilder.ts @@ -5,12 +5,10 @@ import { generalUserServiceInfo } from '../generalUserServiceInfo.js' import { userServiceV1ConfigSchema } from './userServiceConfig.js' export const userServiceInfo = { - serviceVersion: '1', - ...generalUserServiceInfo, + serviceVersion: '1', + ...generalUserServiceInfo, } as const satisfies ServiceInfoType // create a service builder instance and assign service config schema and default config. -export const userV1ServiceBuilder = new ServiceBuilder(userServiceInfo) - .setConfigSchema(userServiceV1ConfigSchema) - .setDefaultConfig({}) +export const userV1ServiceBuilder = new ServiceBuilder(userServiceInfo).setConfigSchema(userServiceV1ConfigSchema) diff --git a/examples/temporal/src/temporal/activities/validate.ts b/examples/temporal/src/temporal/activities/validate.ts index f61313c07..162253235 100644 --- a/examples/temporal/src/temporal/activities/validate.ts +++ b/examples/temporal/src/temporal/activities/validate.ts @@ -2,14 +2,14 @@ import { StatusCode, UnhandledError } from '@purista/core' import { z } from 'zod' export async function validate(data: unknown): Promise> { - const onboardingWorkflowInputSchema = z.object({ - name: z.string(), - email: z.string().email().toLowerCase(), - }) + const onboardingWorkflowInputSchema = z.object({ + name: z.string(), + email: z.string().email().toLowerCase(), + }) - try { - return onboardingWorkflowInputSchema.parse(data) - } catch (err) { - throw UnhandledError.fromError(err, StatusCode.InternalServerError) - } + try { + return onboardingWorkflowInputSchema.parse(data) + } catch (err) { + throw UnhandledError.fromError(err, StatusCode.InternalServerError) + } } diff --git a/examples/temporal/src/temporal/getInvoke.ts b/examples/temporal/src/temporal/getInvoke.ts index 8a6c14582..a8f57082a 100644 --- a/examples/temporal/src/temporal/getInvoke.ts +++ b/examples/temporal/src/temporal/getInvoke.ts @@ -3,32 +3,32 @@ import { Context } from '@temporalio/activity' // a small get which returns the invoke function export const getInvoke = - (eventBridge: EventBridge) => - async ( - serviceName: string, - serviceVersion: string, - serviceTarget: string, - payload: unknown, - parameter = {}, - ): Promise => { - const ctx = Context.current() - return eventBridge.invoke({ - sender: { - serviceName: ctx.info.workflowType, - serviceVersion: '1', - serviceTarget: ctx.info.activityType, - instanceId: eventBridge.instanceId, - }, - receiver: { - serviceName, - serviceVersion, - serviceTarget, - }, - payload: { - payload, - parameter, - }, - contentEncoding: 'application/json', - contentType: 'utf-8', - }) - } + (eventBridge: EventBridge) => + async ( + serviceName: string, + serviceVersion: string, + serviceTarget: string, + payload: unknown, + parameter = {}, + ): Promise => { + const ctx = Context.current() + return eventBridge.invoke({ + sender: { + serviceName: ctx.info.workflowType, + serviceVersion: '1', + serviceTarget: ctx.info.activityType, + instanceId: eventBridge.instanceId, + }, + receiver: { + serviceName, + serviceVersion, + serviceTarget, + }, + payload: { + payload, + parameter, + }, + contentEncoding: 'utf-8', + contentType: 'application/json', + }) + } diff --git a/examples/temporal/src/temporal/worker.ts b/examples/temporal/src/temporal/worker.ts index f27741997..fbd21525a 100644 --- a/examples/temporal/src/temporal/worker.ts +++ b/examples/temporal/src/temporal/worker.ts @@ -4,11 +4,11 @@ import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http' import { Resource } from '@opentelemetry/resources' import { NodeSDK } from '@opentelemetry/sdk-node' import { SimpleSpanProcessor } from '@opentelemetry/sdk-trace-node' -import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions' +import { ATTR_SERVICE_NAME } from '@opentelemetry/semantic-conventions' import type { EventBridge } from '@purista/core' import { initLogger } from '@purista/core' import { NatsBridge } from '@purista/natsbridge' -import { makeWorkflowExporter, OpenTelemetryActivityInboundInterceptor } from '@temporalio/interceptors-opentelemetry' +import { OpenTelemetryActivityInboundInterceptor, makeWorkflowExporter } from '@temporalio/interceptors-opentelemetry' import { NativeConnection, Worker } from '@temporalio/worker' import jaegerExporterOptions from '../config/jaegerExporterOptions.js' // [!code ++] @@ -18,73 +18,73 @@ import * as activities from './activities/index.js' import { getInvoke } from './getInvoke.js' const getPuristaBasedActivities = (eventBridge: EventBridge) => { - const invoke = getInvoke(eventBridge) + const invoke = getInvoke(eventBridge) - return { - createAccount: (input: unknown) => invoke<{ accountId: string }>('Account', '1', 'createAccount', input), - createUser: (input: unknown) => invoke<{ userId: string }>('User', '1', 'createUser', input), - sendEmailVerification: (email: string) => - invoke<{ userId: string }>('Email', '1', 'sendVerificationEmail', { email }), - } + return { + createAccount: (input: unknown) => invoke<{ accountId: string }>('Account', '1', 'createAccount', input), + createUser: (input: unknown) => invoke<{ userId: string }>('User', '1', 'createUser', input), + sendEmailVerification: (email: string) => + invoke<{ userId: string }>('Email', '1', 'sendVerificationEmail', { email }), + } } export type ActivitiesType = typeof activities & ReturnType async function run() { - // setup OpenTelemetry - const resource = new Resource({ - [SemanticResourceAttributes.SERVICE_NAME]: 'temporal-worker', - }) - const exporter = new OTLPTraceExporter(jaegerExporterOptions) - const otel = new NodeSDK({ traceExporter: exporter, resource }) - await otel.start() + // setup OpenTelemetry + const resource = new Resource({ + [ATTR_SERVICE_NAME]: 'temporal-worker', + }) + const exporter = new OTLPTraceExporter(jaegerExporterOptions) + const otel = new NodeSDK({ traceExporter: exporter, resource }) + otel.start() - const spanProcessor = new SimpleSpanProcessor(exporter) - const logger = initLogger('debug') + const spanProcessor = new SimpleSpanProcessor(exporter) + const logger = initLogger('debug') - // inject eventBride and logger - const eventBridge = new NatsBridge({ ...natsBridgeConfig, logger, spanProcessor }) - await eventBridge.start() + // inject eventBride and logger + const eventBridge = new NatsBridge({ ...natsBridgeConfig, logger, spanProcessor }) + await eventBridge.start() - // Step 1: Establish a connection with Temporal server. - // - // Worker code uses `@temporalio/worker.NativeConnection`. - // (But in your application code it's `@temporalio/client.Connection`.) - const connection = await NativeConnection.connect(temporalConfig.connect) - // Step 2: Register Workflows and Activities with the Worker. - const worker = await Worker.create({ - connection, - namespace: temporalConfig.namespace, - taskQueue: temporalConfig.taskQueue, - // Workflows are registered using a path as they run in a separate JS context. - workflowsPath: fileURLToPath(new URL('./workflows', import.meta.url)), - activities: { - ...activities, - ...getPuristaBasedActivities(eventBridge), - }, - interceptors: { - // example contains both workflow and interceptors - workflowModules: [fileURLToPath(new URL('./workflows', import.meta.url))], - activityInbound: [(ctx) => new OpenTelemetryActivityInboundInterceptor(ctx)], - }, - sinks: { - exporter: makeWorkflowExporter(exporter, resource), - }, - }) + // Step 1: Establish a connection with Temporal server. + // + // Worker code uses `@temporalio/worker.NativeConnection`. + // (But in your application code it's `@temporalio/client.Connection`.) + const connection = await NativeConnection.connect(temporalConfig.connect) + // Step 2: Register Workflows and Activities with the Worker. + const worker = await Worker.create({ + connection, + namespace: temporalConfig.namespace, + taskQueue: temporalConfig.taskQueue, + // Workflows are registered using a path as they run in a separate JS context. + workflowsPath: fileURLToPath(new URL('./workflows', import.meta.url)), + activities: { + ...activities, + ...getPuristaBasedActivities(eventBridge), + }, + interceptors: { + // example contains both workflow and interceptors + workflowModules: [fileURLToPath(new URL('./workflows', import.meta.url))], + activityInbound: [ctx => new OpenTelemetryActivityInboundInterceptor(ctx)], + }, + sinks: { + exporter: makeWorkflowExporter(exporter, resource), + }, + }) - // Step 3: Start accepting tasks on the `default-task-queue` queue - // - // The worker runs until it encounters an unexpected error or the process receives a shutdown signal registered on - // the SDK Runtime object. - // - // By default, worker logs are written via the Runtime logger to STDERR at INFO level. - // - // See https://typescript.temporal.io/api/classes/worker.Runtime#install to customize these defaults. - await worker.run() + // Step 3: Start accepting tasks on the `default-task-queue` queue + // + // The worker runs until it encounters an unexpected error or the process receives a shutdown signal registered on + // the SDK Runtime object. + // + // By default, worker logs are written via the Runtime logger to STDERR at INFO level. + // + // See https://typescript.temporal.io/api/classes/worker.Runtime#install to customize these defaults. + await worker.run() } -run().catch((err) => { - // eslint-disable-next-line no-console - console.error(err) - process.exit(1) +run().catch(err => { + // biome-ignore lint/suspicious/noConsole: no logger available + console.error(err) + process.exit(1) }) diff --git a/examples/temporal/src/temporal/workflows/interceptors.ts b/examples/temporal/src/temporal/workflows/interceptors.ts index fa58bc7d5..f36979c83 100644 --- a/examples/temporal/src/temporal/workflows/interceptors.ts +++ b/examples/temporal/src/temporal/workflows/interceptors.ts @@ -1,10 +1,10 @@ import { - OpenTelemetryInboundInterceptor, - OpenTelemetryOutboundInterceptor, + OpenTelemetryInboundInterceptor, + OpenTelemetryOutboundInterceptor, } from '@temporalio/interceptors-opentelemetry/lib/workflow' import type { WorkflowInterceptorsFactory } from '@temporalio/workflow' export const interceptors: WorkflowInterceptorsFactory = () => ({ - inbound: [new OpenTelemetryInboundInterceptor()], - outbound: [new OpenTelemetryOutboundInterceptor()], + inbound: [new OpenTelemetryInboundInterceptor()], + outbound: [new OpenTelemetryOutboundInterceptor()], }) diff --git a/examples/temporal/src/temporal/workflows/onboardingWorkflow.ts b/examples/temporal/src/temporal/workflows/onboardingWorkflow.ts index c211b7303..907da9da0 100644 --- a/examples/temporal/src/temporal/workflows/onboardingWorkflow.ts +++ b/examples/temporal/src/temporal/workflows/onboardingWorkflow.ts @@ -5,31 +5,31 @@ import { condition, defineSignal, proxyActivities, setHandler } from '@temporali import type { ActivitiesType } from '../worker.js' propagation.setGlobalPropagator( - new CompositePropagator({ - propagators: [new W3CTraceContextPropagator(), new W3CBaggagePropagator()], - }), + new CompositePropagator({ + propagators: [new W3CTraceContextPropagator(), new W3CBaggagePropagator()], + }), ) const { createAccount, validate, sendEmailVerification, createUser } = proxyActivities({ - startToCloseTimeout: '1 minute', + startToCloseTimeout: '1 minute', }) const emailVerifiedSignal = defineSignal('signal-email-verified') /** A workflow that simply calls an activity */ export async function onboardingWorkflow(input: unknown): Promise { - const register = await validate(input) + const register = await validate(input) - await sendEmailVerification(register.email) + await sendEmailVerification(register.email) - let isEmailVerified = false - setHandler(emailVerifiedSignal, async () => { - isEmailVerified = true - }) + let isEmailVerified = false + setHandler(emailVerifiedSignal, async () => { + isEmailVerified = true + }) - await condition(() => isEmailVerified, '60 minutes') + await condition(() => isEmailVerified, '60 minutes') - const user = await createUser(register) - const _account = await createAccount(user) - // const card = await issueCardForAccount(account) + const user = await createUser(register) + const _account = await createAccount(user) + // const card = await issueCardForAccount(account) } diff --git a/examples/temporal/tsconfig.json b/examples/temporal/tsconfig.json index 2be7bb8a3..47cd2e59e 100644 --- a/examples/temporal/tsconfig.json +++ b/examples/temporal/tsconfig.json @@ -1,38 +1,29 @@ { - "$schema": "https://json.schemastore.org/tsconfig", - "display": "Node 18", - "compilerOptions": { - "outDir": "dist", - "strict": true, - "module": "es2022", - "declaration": true, - "removeComments": false, - "emitDecoratorMetadata": true, - "experimentalDecorators": true, - "moduleResolution": "Node", - "allowSyntheticDefaultImports": true, - "target": "es2022", - "sourceMap": true, - "incremental": true, - "noImplicitAny": true, - "esModuleInterop": true, - "resolveJsonModule": true, - "skipLibCheck": true, - "forceConsistentCasingInFileNames": true, - "declarationMap": true, - "noFallthroughCasesInSwitch": true, - "useUnknownInCatchVariables": true, - "types": [ - "node", - "vitest/globals" - ], - }, - "include": [ - "./src/**/*", - "./test/*", - ], - "exclude": [ - "node_modules", - "dist" - ] -} \ No newline at end of file + "$schema": "https://json.schemastore.org/tsconfig", + "display": "Node 18", + "compilerOptions": { + "outDir": "dist", + "strict": true, + "module": "es2022", + "declaration": true, + "removeComments": false, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "moduleResolution": "Node", + "allowSyntheticDefaultImports": true, + "target": "es2022", + "sourceMap": true, + "incremental": true, + "noImplicitAny": true, + "esModuleInterop": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "declarationMap": true, + "noFallthroughCasesInSwitch": true, + "useUnknownInCatchVariables": true, + "types": ["node", "vitest/globals"] + }, + "include": ["./src/**/*", "./test/*"], + "exclude": ["node_modules", "dist"] +} diff --git a/examples/temporal/vite.config.ts b/examples/temporal/vite.config.ts index 3e3c4a64d..0146a3a73 100644 --- a/examples/temporal/vite.config.ts +++ b/examples/temporal/vite.config.ts @@ -1,13 +1,13 @@ import { defineConfig } from 'vitest/config' export default defineConfig({ - test: { - globals: true, - watch: false, - environment: 'node', - coverage: { - enabled: true, - include: ['**/src/**/*.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], - }, - }, + test: { + globals: true, + watch: false, + environment: 'node', + coverage: { + enabled: true, + include: ['**/src/**/*.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], + }, + }, }) diff --git a/package-lock.json b/package-lock.json index 9fce0d231..dd90ed001 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,17809 +1,23413 @@ { - "name": "purista", - "version": "1.11.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "version": "1.11.0", - "workspaces": [ - "./packages/*", - "./examples/*", - "website" - ], - "devDependencies": { - "@types/node": "^20.11.17", - "@types/sinon": "^17.0.3", - "@typescript-eslint/eslint-plugin": "^7.0.1", - "@typescript-eslint/parser": "^7.0.1", - "@vitest/coverage-v8": "^1.3.0", - "eslint": "^8.56.0", - "eslint-config-prettier": "^9.1.0", - "eslint-config-standard": "^17.1.0", - "eslint-plugin-import": "^2.29.1", - "eslint-plugin-import-esm": "^2.0.0", - "eslint-plugin-json": "^3.1.0", - "eslint-plugin-node": "^11.1.0", - "eslint-plugin-prettier": "5.1.3", - "eslint-plugin-simple-import-sort": "^12.0.0", - "eslint-plugin-vitest": "^0.3.22", - "git-cliff": "^2.0.4", - "prettier": "^3.2.4", - "rimraf": "^5.0.5", - "sinon": "^17.0.1", - "testcontainers": "^10.6.0", - "ts-node": "^10.9.2", - "tsx": "^4.7.0", - "typedoc": "^0.25.8", - "typedoc-plugin-markdown": "^3.17.1", - "typescript": "^5.3.3", - "vitest": "^1.3.0" - } - }, - "examples/dapr-example": { - "name": "@purista/dapr-example", - "version": "1.11.0", - "license": "ISC", - "dependencies": { - "@hono/node-server": "^1.8.0", - "@opentelemetry/exporter-zipkin": "^1.19.0", - "@opentelemetry/sdk-trace-node": "^1.19.0", - "@purista/core": "*", - "@purista/dapr-sdk": "*", - "zod": "^3.22.4" - }, - "devDependencies": { - "@types/node": "^20.11.17", - "pino-pretty": "^10.3.1", - "sinon": "^17.0.1", - "tsx": "^4.7.0", - "typescript": "^5.3.3", - "vitest": "^1.3.0" - }, - "engines": { - "node": ">=18.15" - } - }, - "examples/fullexample": { - "name": "@purista/full-example", - "version": "1.11.0", - "license": "ISC", - "dependencies": { - "@fastify/static": "^7.0.1", - "@opentelemetry/exporter-trace-otlp-http": "^0.48.0", - "@opentelemetry/exporter-zipkin": "^1.19.0", - "@opentelemetry/sdk-trace-node": "^1.19.0", - "@purista/amqpbridge": "*", - "@purista/core": "*", - "@purista/httpserver": "*", - "@purista/redis-state-store": "*", - "@uptrace/node": "^1.19.0", - "pino-loki": "^2.2.1", - "pino-pretty": "^10.3.1", - "zod": "^3.22.4" - }, - "devDependencies": { - "@types/node": "^20.11.17", - "sinon": "^17.0.1", - "tsx": "^4.7.0", - "typescript": "^5.3.3", - "vitest": "^1.3.0" - }, - "engines": { - "node": ">=18.15" - } - }, - "examples/hono-example": { - "name": "@purista/hono-example", - "version": "1.11.0", - "license": "ISC", - "dependencies": { - "@hono/node-server": "^1.8.0", - "@hono/swagger-ui": "^0.2.1", - "@purista/core": "*", - "@purista/hono-http-server": "^1.9.0", - "zod": "3.22.4" - }, - "devDependencies": { - "@types/node": "^20.11.17", - "pino-pretty": "^10.3.1", - "sinon": "^17.0.1", - "tsx": "^4.7.0", - "typescript": "^5.3.3", - "vitest": "^1.3.0" - }, - "engines": { - "node": ">=18.15" - } - }, - "examples/kubernetes": { - "name": "@purista/kubernetes-example", - "version": "1.11.0", - "license": "ISC", - "dependencies": { - "@hono/node-server": "^1.8.0", - "@opentelemetry/exporter-trace-otlp-http": "^0.48.0", - "@purista/amqpbridge": "*", - "@purista/core": "*", - "@purista/k8s-sdk": "*", - "zod": "^3.22.4" - }, - "devDependencies": { - "@types/node": "^20.11.17", - "pino-pretty": "^10.3.1", - "sinon": "^17.0.1", - "tsx": "^4.7.0", - "typescript": "^5.3.3", - "vitest": "^1.3.0" - }, - "engines": { - "node": ">=18.15" - } - }, - "examples/mqtt-bridge": { - "name": "@purista/mqtt-example", - "version": "1.11.0", - "license": "ISC", - "dependencies": { - "@fastify/static": "^7.0.1", - "@purista/core": "*", - "@purista/httpserver": "*", - "@purista/mqttbridge": "*", - "zod": "3.22.4" - }, - "devDependencies": { - "@types/node": "^20.11.17", - "pino-pretty": "^10.3.1", - "sinon": "^17.0.1", - "tsx": "^4.7.0", - "typescript": "^5.3.3", - "vitest": "^1.3.0" - }, - "engines": { - "node": ">=18.15" - } - }, - "examples/nats-bridge": { - "name": "@purista/nats-example", - "version": "1.11.0", - "license": "ISC", - "dependencies": { - "@fastify/static": "^7.0.1", - "@purista/core": "*", - "@purista/httpserver": "*", - "@purista/nats-state-store": "*", - "@purista/natsbridge": "*", - "zod": "^3.22.4" - }, - "devDependencies": { - "@types/node": "^20.11.17", - "pino-pretty": "^10.3.1", - "sinon": "^17.0.1", - "tsx": "^4.7.0", - "typescript": "^5.3.3", - "vitest": "^1.3.0" - }, - "engines": { - "node": ">=18.15" - } - }, - "examples/quickstart": { - "name": "@purista/quickstart", - "version": "1.11.0", - "license": "ISC", - "dependencies": { - "@fastify/static": "^7.0.1", - "@purista/core": "*", - "@purista/httpserver": "*", - "zod": "3.22.4" - }, - "devDependencies": { - "@types/node": "^20.11.17", - "pino-pretty": "^10.3.1", - "sinon": "^17.0.1", - "tsx": "^4.7.0", - "typescript": "^5.3.3", - "vitest": "^1.3.0" - }, - "engines": { - "node": ">=18.15" - } - }, - "examples/temporal": { - "name": "@purista/temporal-example", - "version": "1.11.0", - "dependencies": { - "@hono/node-server": "^1.8.0", - "@hono/swagger-ui": "^0.2.1", - "@opentelemetry/exporter-trace-otlp-http": "^0.48.0", - "@opentelemetry/sdk-trace-node": "^1.21.0", - "@purista/core": "*", - "@purista/hono-http-server": "*", - "@purista/natsbridge": "*", - "@temporalio/activity": "^1.9.1", - "@temporalio/client": "^1.9.1", - "@temporalio/interceptors-opentelemetry": "^1.9.1", - "@temporalio/worker": "^1.9.1", - "@temporalio/workflow": "^1.9.1", - "zod": "^3.22.4" - }, - "devDependencies": { - "@types/node": "^20.11.19", - "@types/sinon": "^17.0.3", - "pino-pretty": "^10.3.1", - "prettier": "^3.2.5", - "sinon": "^17.0.1", - "tsx": "^4.7.1", - "typescript": "^5.3.3", - "vitest": "^1.3.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@aashutoshrathi/word-wrap": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", - "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@acuminous/bitsyntax": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@acuminous/bitsyntax/-/bitsyntax-0.1.2.tgz", - "integrity": "sha512-29lUK80d1muEQqiUsSo+3A0yP6CdspgC95EnKBMi22Xlwt79i/En4Vr67+cXhU+cZjbti3TgGGC5wy1stIywVQ==", - "dependencies": { - "buffer-more-ints": "~1.0.0", - "debug": "^4.3.4", - "safe-buffer": "~5.1.2" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/@acuminous/bitsyntax/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "node_modules/@algolia/autocomplete-core": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.9.3.tgz", - "integrity": "sha512-009HdfugtGCdC4JdXUbVJClA0q0zh24yyePn+KUGk3rP7j8FEe/m5Yo/z65gn6nP/cM39PxpzqKrL7A6fP6PPw==", - "dev": true, - "dependencies": { - "@algolia/autocomplete-plugin-algolia-insights": "1.9.3", - "@algolia/autocomplete-shared": "1.9.3" - } - }, - "node_modules/@algolia/autocomplete-plugin-algolia-insights": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.9.3.tgz", - "integrity": "sha512-a/yTUkcO/Vyy+JffmAnTWbr4/90cLzw+CC3bRbhnULr/EM0fGNvM13oQQ14f2moLMcVDyAx/leczLlAOovhSZg==", - "dev": true, - "dependencies": { - "@algolia/autocomplete-shared": "1.9.3" - }, - "peerDependencies": { - "search-insights": ">= 1 < 3" - } - }, - "node_modules/@algolia/autocomplete-preset-algolia": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.9.3.tgz", - "integrity": "sha512-d4qlt6YmrLMYy95n5TB52wtNDr6EgAIPH81dvvvW8UmuWRgxEtY0NJiPwl/h95JtG2vmRM804M0DSwMCNZlzRA==", - "dev": true, - "dependencies": { - "@algolia/autocomplete-shared": "1.9.3" - }, - "peerDependencies": { - "@algolia/client-search": ">= 4.9.1 < 6", - "algoliasearch": ">= 4.9.1 < 6" - } - }, - "node_modules/@algolia/autocomplete-shared": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.9.3.tgz", - "integrity": "sha512-Wnm9E4Ye6Rl6sTTqjoymD+l8DjSTHsHboVRYrKgEt8Q7UHm9nYbqhN/i0fhUYA3OAEH7WA8x3jfpnmJm3rKvaQ==", - "dev": true, - "peerDependencies": { - "@algolia/client-search": ">= 4.9.1 < 6", - "algoliasearch": ">= 4.9.1 < 6" - } - }, - "node_modules/@algolia/cache-browser-local-storage": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.22.1.tgz", - "integrity": "sha512-Sw6IAmOCvvP6QNgY9j+Hv09mvkvEIDKjYW8ow0UDDAxSXy664RBNQk3i/0nt7gvceOJ6jGmOTimaZoY1THmU7g==", - "dev": true, - "dependencies": { - "@algolia/cache-common": "4.22.1" - } - }, - "node_modules/@algolia/cache-common": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/cache-common/-/cache-common-4.22.1.tgz", - "integrity": "sha512-TJMBKqZNKYB9TptRRjSUtevJeQVXRmg6rk9qgFKWvOy8jhCPdyNZV1nB3SKGufzvTVbomAukFR8guu/8NRKBTA==", - "dev": true - }, - "node_modules/@algolia/cache-in-memory": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/cache-in-memory/-/cache-in-memory-4.22.1.tgz", - "integrity": "sha512-ve+6Ac2LhwpufuWavM/aHjLoNz/Z/sYSgNIXsinGofWOysPilQZPUetqLj8vbvi+DHZZaYSEP9H5SRVXnpsNNw==", - "dev": true, - "dependencies": { - "@algolia/cache-common": "4.22.1" - } - }, - "node_modules/@algolia/client-account": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/client-account/-/client-account-4.22.1.tgz", - "integrity": "sha512-k8m+oegM2zlns/TwZyi4YgCtyToackkOpE+xCaKCYfBfDtdGOaVZCM5YvGPtK+HGaJMIN/DoTL8asbM3NzHonw==", - "dev": true, - "dependencies": { - "@algolia/client-common": "4.22.1", - "@algolia/client-search": "4.22.1", - "@algolia/transporter": "4.22.1" - } - }, - "node_modules/@algolia/client-analytics": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-4.22.1.tgz", - "integrity": "sha512-1ssi9pyxyQNN4a7Ji9R50nSdISIumMFDwKNuwZipB6TkauJ8J7ha/uO60sPJFqQyqvvI+px7RSNRQT3Zrvzieg==", - "dev": true, - "dependencies": { - "@algolia/client-common": "4.22.1", - "@algolia/client-search": "4.22.1", - "@algolia/requester-common": "4.22.1", - "@algolia/transporter": "4.22.1" - } - }, - "node_modules/@algolia/client-common": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.22.1.tgz", - "integrity": "sha512-IvaL5v9mZtm4k4QHbBGDmU3wa/mKokmqNBqPj0K7lcR8ZDKzUorhcGp/u8PkPC/e0zoHSTvRh7TRkGX3Lm7iOQ==", - "dev": true, - "dependencies": { - "@algolia/requester-common": "4.22.1", - "@algolia/transporter": "4.22.1" - } - }, - "node_modules/@algolia/client-personalization": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-4.22.1.tgz", - "integrity": "sha512-sl+/klQJ93+4yaqZ7ezOttMQ/nczly/3GmgZXJ1xmoewP5jmdP/X/nV5U7EHHH3hCUEHeN7X1nsIhGPVt9E1cQ==", - "dev": true, - "dependencies": { - "@algolia/client-common": "4.22.1", - "@algolia/requester-common": "4.22.1", - "@algolia/transporter": "4.22.1" - } - }, - "node_modules/@algolia/client-search": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.22.1.tgz", - "integrity": "sha512-yb05NA4tNaOgx3+rOxAmFztgMTtGBi97X7PC3jyNeGiwkAjOZc2QrdZBYyIdcDLoI09N0gjtpClcackoTN0gPA==", - "dev": true, - "dependencies": { - "@algolia/client-common": "4.22.1", - "@algolia/requester-common": "4.22.1", - "@algolia/transporter": "4.22.1" - } - }, - "node_modules/@algolia/logger-common": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/logger-common/-/logger-common-4.22.1.tgz", - "integrity": "sha512-OnTFymd2odHSO39r4DSWRFETkBufnY2iGUZNrMXpIhF5cmFE8pGoINNPzwg02QLBlGSaLqdKy0bM8S0GyqPLBg==", - "dev": true - }, - "node_modules/@algolia/logger-console": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/logger-console/-/logger-console-4.22.1.tgz", - "integrity": "sha512-O99rcqpVPKN1RlpgD6H3khUWylU24OXlzkavUAMy6QZd1776QAcauE3oP8CmD43nbaTjBexZj2nGsBH9Tc0FVA==", - "dev": true, - "dependencies": { - "@algolia/logger-common": "4.22.1" - } - }, - "node_modules/@algolia/requester-browser-xhr": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.22.1.tgz", - "integrity": "sha512-dtQGYIg6MteqT1Uay3J/0NDqD+UciHy3QgRbk7bNddOJu+p3hzjTRYESqEnoX/DpEkaNYdRHUKNylsqMpgwaEw==", - "dev": true, - "dependencies": { - "@algolia/requester-common": "4.22.1" - } - }, - "node_modules/@algolia/requester-common": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/requester-common/-/requester-common-4.22.1.tgz", - "integrity": "sha512-dgvhSAtg2MJnR+BxrIFqlLtkLlVVhas9HgYKMk2Uxiy5m6/8HZBL40JVAMb2LovoPFs9I/EWIoFVjOrFwzn5Qg==", - "dev": true - }, - "node_modules/@algolia/requester-node-http": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.22.1.tgz", - "integrity": "sha512-JfmZ3MVFQkAU+zug8H3s8rZ6h0ahHZL/SpMaSasTCGYR5EEJsCc8SI5UZ6raPN2tjxa5bxS13BRpGSBUens7EA==", - "dev": true, - "dependencies": { - "@algolia/requester-common": "4.22.1" - } - }, - "node_modules/@algolia/transporter": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/transporter/-/transporter-4.22.1.tgz", - "integrity": "sha512-kzWgc2c9IdxMa3YqA6TN0NW5VrKYYW/BELIn7vnLyn+U/RFdZ4lxxt9/8yq3DKV5snvoDzzO4ClyejZRdV3lMQ==", - "dev": true, - "dependencies": { - "@algolia/cache-common": "4.22.1", - "@algolia/logger-common": "4.22.1", - "@algolia/requester-common": "4.22.1" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", - "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", - "dev": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@aws-crypto/crc32": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-3.0.0.tgz", - "integrity": "sha512-IzSgsrxUcsrejQbPVilIKy16kAT52EwB6zSaI+M3xxIhKh5+aldEyvI+z6erM7TCLB2BJsFrtHjp6/4/sr+3dA==", - "dependencies": { - "@aws-crypto/util": "^3.0.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^1.11.1" - } - }, - "node_modules/@aws-crypto/crc32/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, - "node_modules/@aws-crypto/ie11-detection": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-3.0.0.tgz", - "integrity": "sha512-341lBBkiY1DfDNKai/wXM3aujNBkXR7tq1URPQDL9wi3AUbI80NR74uF1TXHMm7po1AcnFk8iu2S2IeU/+/A+Q==", - "dependencies": { - "tslib": "^1.11.1" - } - }, - "node_modules/@aws-crypto/ie11-detection/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, - "node_modules/@aws-crypto/sha256-browser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-3.0.0.tgz", - "integrity": "sha512-8VLmW2B+gjFbU5uMeqtQM6Nj0/F1bro80xQXCW6CQBWgosFWXTx77aeOF5CAIAmbOK64SdMBJdNr6J41yP5mvQ==", - "dependencies": { - "@aws-crypto/ie11-detection": "^3.0.0", - "@aws-crypto/sha256-js": "^3.0.0", - "@aws-crypto/supports-web-crypto": "^3.0.0", - "@aws-crypto/util": "^3.0.0", - "@aws-sdk/types": "^3.222.0", - "@aws-sdk/util-locate-window": "^3.0.0", - "@aws-sdk/util-utf8-browser": "^3.0.0", - "tslib": "^1.11.1" - } - }, - "node_modules/@aws-crypto/sha256-browser/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, - "node_modules/@aws-crypto/sha256-js": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-3.0.0.tgz", - "integrity": "sha512-PnNN7os0+yd1XvXAy23CFOmTbMaDxgxXtTKHybrJ39Y8kGzBATgBFibWJKH6BhytLI/Zyszs87xCOBNyBig6vQ==", - "dependencies": { - "@aws-crypto/util": "^3.0.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^1.11.1" - } - }, - "node_modules/@aws-crypto/sha256-js/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, - "node_modules/@aws-crypto/supports-web-crypto": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-3.0.0.tgz", - "integrity": "sha512-06hBdMwUAb2WFTuGG73LSC0wfPu93xWwo5vL2et9eymgmu3Id5vFAHBbajVWiGhPO37qcsdCap/FqXvJGJWPIg==", - "dependencies": { - "tslib": "^1.11.1" - } - }, - "node_modules/@aws-crypto/supports-web-crypto/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, - "node_modules/@aws-crypto/util": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-3.0.0.tgz", - "integrity": "sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w==", - "dependencies": { - "@aws-sdk/types": "^3.222.0", - "@aws-sdk/util-utf8-browser": "^3.0.0", - "tslib": "^1.11.1" - } - }, - "node_modules/@aws-crypto/util/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, - "node_modules/@aws-sdk/client-secrets-manager": { - "version": "3.521.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-secrets-manager/-/client-secrets-manager-3.521.0.tgz", - "integrity": "sha512-C01O7Ep06lL3pEhvsEwLjCHTt4ARA5NmCqQlVg/NVPkWTA3RX/GgvaqKu+HMFeTRoipTdOvdril+weBLIWFKAg==", - "dependencies": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/client-sts": "3.521.0", - "@aws-sdk/core": "3.521.0", - "@aws-sdk/credential-provider-node": "3.521.0", - "@aws-sdk/middleware-host-header": "3.521.0", - "@aws-sdk/middleware-logger": "3.521.0", - "@aws-sdk/middleware-recursion-detection": "3.521.0", - "@aws-sdk/middleware-user-agent": "3.521.0", - "@aws-sdk/region-config-resolver": "3.521.0", - "@aws-sdk/types": "3.521.0", - "@aws-sdk/util-endpoints": "3.521.0", - "@aws-sdk/util-user-agent-browser": "3.521.0", - "@aws-sdk/util-user-agent-node": "3.521.0", - "@smithy/config-resolver": "^2.1.2", - "@smithy/core": "^1.3.3", - "@smithy/fetch-http-handler": "^2.4.2", - "@smithy/hash-node": "^2.1.2", - "@smithy/invalid-dependency": "^2.1.2", - "@smithy/middleware-content-length": "^2.1.2", - "@smithy/middleware-endpoint": "^2.4.2", - "@smithy/middleware-retry": "^2.1.2", - "@smithy/middleware-serde": "^2.1.2", - "@smithy/middleware-stack": "^2.1.2", - "@smithy/node-config-provider": "^2.2.2", - "@smithy/node-http-handler": "^2.4.0", - "@smithy/protocol-http": "^3.2.0", - "@smithy/smithy-client": "^2.4.0", - "@smithy/types": "^2.10.0", - "@smithy/url-parser": "^2.1.2", - "@smithy/util-base64": "^2.1.1", - "@smithy/util-body-length-browser": "^2.1.1", - "@smithy/util-body-length-node": "^2.2.1", - "@smithy/util-defaults-mode-browser": "^2.1.2", - "@smithy/util-defaults-mode-node": "^2.2.1", - "@smithy/util-endpoints": "^1.1.2", - "@smithy/util-middleware": "^2.1.2", - "@smithy/util-retry": "^2.1.2", - "@smithy/util-utf8": "^2.1.1", - "tslib": "^2.5.0", - "uuid": "^9.0.1" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-ssm": { - "version": "3.521.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-ssm/-/client-ssm-3.521.0.tgz", - "integrity": "sha512-xAxzLIAkPVilEseOwDNtmH6YiTjTfarodhzKli1xz9B/GUAaQN6mXNMUOhtwcu5etMeG7xlw8CBbzf9ZVBrTGw==", - "dependencies": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/client-sts": "3.521.0", - "@aws-sdk/core": "3.521.0", - "@aws-sdk/credential-provider-node": "3.521.0", - "@aws-sdk/middleware-host-header": "3.521.0", - "@aws-sdk/middleware-logger": "3.521.0", - "@aws-sdk/middleware-recursion-detection": "3.521.0", - "@aws-sdk/middleware-user-agent": "3.521.0", - "@aws-sdk/region-config-resolver": "3.521.0", - "@aws-sdk/types": "3.521.0", - "@aws-sdk/util-endpoints": "3.521.0", - "@aws-sdk/util-user-agent-browser": "3.521.0", - "@aws-sdk/util-user-agent-node": "3.521.0", - "@smithy/config-resolver": "^2.1.2", - "@smithy/core": "^1.3.3", - "@smithy/fetch-http-handler": "^2.4.2", - "@smithy/hash-node": "^2.1.2", - "@smithy/invalid-dependency": "^2.1.2", - "@smithy/middleware-content-length": "^2.1.2", - "@smithy/middleware-endpoint": "^2.4.2", - "@smithy/middleware-retry": "^2.1.2", - "@smithy/middleware-serde": "^2.1.2", - "@smithy/middleware-stack": "^2.1.2", - "@smithy/node-config-provider": "^2.2.2", - "@smithy/node-http-handler": "^2.4.0", - "@smithy/protocol-http": "^3.2.0", - "@smithy/smithy-client": "^2.4.0", - "@smithy/types": "^2.10.0", - "@smithy/url-parser": "^2.1.2", - "@smithy/util-base64": "^2.1.1", - "@smithy/util-body-length-browser": "^2.1.1", - "@smithy/util-body-length-node": "^2.2.1", - "@smithy/util-defaults-mode-browser": "^2.1.2", - "@smithy/util-defaults-mode-node": "^2.2.1", - "@smithy/util-endpoints": "^1.1.2", - "@smithy/util-middleware": "^2.1.2", - "@smithy/util-retry": "^2.1.2", - "@smithy/util-utf8": "^2.1.1", - "@smithy/util-waiter": "^2.1.2", - "tslib": "^2.5.0", - "uuid": "^9.0.1" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-sso": { - "version": "3.521.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.521.0.tgz", - "integrity": "sha512-aEx8kEvWmTwCja6hvIZd5PvxHsI1HQZkckXhw1UrkDPnfcAwQoQAgselI7D+PVT5qQDIjXRm0NpsvBLaLj6jZw==", - "dependencies": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/core": "3.521.0", - "@aws-sdk/middleware-host-header": "3.521.0", - "@aws-sdk/middleware-logger": "3.521.0", - "@aws-sdk/middleware-recursion-detection": "3.521.0", - "@aws-sdk/middleware-user-agent": "3.521.0", - "@aws-sdk/region-config-resolver": "3.521.0", - "@aws-sdk/types": "3.521.0", - "@aws-sdk/util-endpoints": "3.521.0", - "@aws-sdk/util-user-agent-browser": "3.521.0", - "@aws-sdk/util-user-agent-node": "3.521.0", - "@smithy/config-resolver": "^2.1.2", - "@smithy/core": "^1.3.3", - "@smithy/fetch-http-handler": "^2.4.2", - "@smithy/hash-node": "^2.1.2", - "@smithy/invalid-dependency": "^2.1.2", - "@smithy/middleware-content-length": "^2.1.2", - "@smithy/middleware-endpoint": "^2.4.2", - "@smithy/middleware-retry": "^2.1.2", - "@smithy/middleware-serde": "^2.1.2", - "@smithy/middleware-stack": "^2.1.2", - "@smithy/node-config-provider": "^2.2.2", - "@smithy/node-http-handler": "^2.4.0", - "@smithy/protocol-http": "^3.2.0", - "@smithy/smithy-client": "^2.4.0", - "@smithy/types": "^2.10.0", - "@smithy/url-parser": "^2.1.2", - "@smithy/util-base64": "^2.1.1", - "@smithy/util-body-length-browser": "^2.1.1", - "@smithy/util-body-length-node": "^2.2.1", - "@smithy/util-defaults-mode-browser": "^2.1.2", - "@smithy/util-defaults-mode-node": "^2.2.1", - "@smithy/util-endpoints": "^1.1.2", - "@smithy/util-middleware": "^2.1.2", - "@smithy/util-retry": "^2.1.2", - "@smithy/util-utf8": "^2.1.1", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc": { - "version": "3.521.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.521.0.tgz", - "integrity": "sha512-MhX0CjV/543MR7DRPr3lA4ZDpGGKopp8cyV4EkSGXB7LMN//eFKKDhuZDlpgWU+aFe2A3DIqlNJjqgs08W0cSA==", - "dependencies": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/client-sts": "3.521.0", - "@aws-sdk/core": "3.521.0", - "@aws-sdk/middleware-host-header": "3.521.0", - "@aws-sdk/middleware-logger": "3.521.0", - "@aws-sdk/middleware-recursion-detection": "3.521.0", - "@aws-sdk/middleware-user-agent": "3.521.0", - "@aws-sdk/region-config-resolver": "3.521.0", - "@aws-sdk/types": "3.521.0", - "@aws-sdk/util-endpoints": "3.521.0", - "@aws-sdk/util-user-agent-browser": "3.521.0", - "@aws-sdk/util-user-agent-node": "3.521.0", - "@smithy/config-resolver": "^2.1.2", - "@smithy/core": "^1.3.3", - "@smithy/fetch-http-handler": "^2.4.2", - "@smithy/hash-node": "^2.1.2", - "@smithy/invalid-dependency": "^2.1.2", - "@smithy/middleware-content-length": "^2.1.2", - "@smithy/middleware-endpoint": "^2.4.2", - "@smithy/middleware-retry": "^2.1.2", - "@smithy/middleware-serde": "^2.1.2", - "@smithy/middleware-stack": "^2.1.2", - "@smithy/node-config-provider": "^2.2.2", - "@smithy/node-http-handler": "^2.4.0", - "@smithy/protocol-http": "^3.2.0", - "@smithy/smithy-client": "^2.4.0", - "@smithy/types": "^2.10.0", - "@smithy/url-parser": "^2.1.2", - "@smithy/util-base64": "^2.1.1", - "@smithy/util-body-length-browser": "^2.1.1", - "@smithy/util-body-length-node": "^2.2.1", - "@smithy/util-defaults-mode-browser": "^2.1.2", - "@smithy/util-defaults-mode-node": "^2.2.1", - "@smithy/util-endpoints": "^1.1.2", - "@smithy/util-middleware": "^2.1.2", - "@smithy/util-retry": "^2.1.2", - "@smithy/util-utf8": "^2.1.1", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "@aws-sdk/credential-provider-node": "^3.521.0" - } - }, - "node_modules/@aws-sdk/client-sts": { - "version": "3.521.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.521.0.tgz", - "integrity": "sha512-f1J5NDbntcwIHJqhks89sQvk7UXPmN0X0BZ2mgpj6pWP+NlPqy+1t1bia8qRhEuNITaEigoq6rqe9xaf4FdY9A==", - "dependencies": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/core": "3.521.0", - "@aws-sdk/middleware-host-header": "3.521.0", - "@aws-sdk/middleware-logger": "3.521.0", - "@aws-sdk/middleware-recursion-detection": "3.521.0", - "@aws-sdk/middleware-user-agent": "3.521.0", - "@aws-sdk/region-config-resolver": "3.521.0", - "@aws-sdk/types": "3.521.0", - "@aws-sdk/util-endpoints": "3.521.0", - "@aws-sdk/util-user-agent-browser": "3.521.0", - "@aws-sdk/util-user-agent-node": "3.521.0", - "@smithy/config-resolver": "^2.1.2", - "@smithy/core": "^1.3.3", - "@smithy/fetch-http-handler": "^2.4.2", - "@smithy/hash-node": "^2.1.2", - "@smithy/invalid-dependency": "^2.1.2", - "@smithy/middleware-content-length": "^2.1.2", - "@smithy/middleware-endpoint": "^2.4.2", - "@smithy/middleware-retry": "^2.1.2", - "@smithy/middleware-serde": "^2.1.2", - "@smithy/middleware-stack": "^2.1.2", - "@smithy/node-config-provider": "^2.2.2", - "@smithy/node-http-handler": "^2.4.0", - "@smithy/protocol-http": "^3.2.0", - "@smithy/smithy-client": "^2.4.0", - "@smithy/types": "^2.10.0", - "@smithy/url-parser": "^2.1.2", - "@smithy/util-base64": "^2.1.1", - "@smithy/util-body-length-browser": "^2.1.1", - "@smithy/util-body-length-node": "^2.2.1", - "@smithy/util-defaults-mode-browser": "^2.1.2", - "@smithy/util-defaults-mode-node": "^2.2.1", - "@smithy/util-endpoints": "^1.1.2", - "@smithy/util-middleware": "^2.1.2", - "@smithy/util-retry": "^2.1.2", - "@smithy/util-utf8": "^2.1.1", - "fast-xml-parser": "4.2.5", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "@aws-sdk/credential-provider-node": "^3.521.0" - } - }, - "node_modules/@aws-sdk/core": { - "version": "3.521.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.521.0.tgz", - "integrity": "sha512-KovKmW7yg/P2HVG2dhV2DAJLyoeGelgsnSGHaktXo/josJ3vDGRNqqRSgVaqKFxnD98dPEMLrjkzZumNUNGvLw==", - "dependencies": { - "@smithy/core": "^1.3.3", - "@smithy/protocol-http": "^3.2.0", - "@smithy/signature-v4": "^2.1.1", - "@smithy/smithy-client": "^2.4.0", - "@smithy/types": "^2.10.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.521.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.521.0.tgz", - "integrity": "sha512-OwblTJNdDAoqYVwcNfhlKDp5z+DINrjBfC6ZjNdlJpTXgxT3IqzuilTJTlydQ+2eG7aXfV9OwTVRQWdCmzFuKA==", - "dependencies": { - "@aws-sdk/types": "3.521.0", - "@smithy/property-provider": "^2.1.1", - "@smithy/types": "^2.10.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-http": { - "version": "3.521.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.521.0.tgz", - "integrity": "sha512-yJM1yNGj2XFH8v6/ffWrFY5nC3/2+8qZ8c4mMMwZru8bYXeuSV4+NNfE59HUWvkAF7xP76u4gr4I8kNrMPTlfg==", - "dependencies": { - "@aws-sdk/types": "3.521.0", - "@smithy/fetch-http-handler": "^2.4.2", - "@smithy/node-http-handler": "^2.4.0", - "@smithy/property-provider": "^2.1.1", - "@smithy/protocol-http": "^3.2.0", - "@smithy/smithy-client": "^2.4.0", - "@smithy/types": "^2.10.0", - "@smithy/util-stream": "^2.1.2", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.521.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.521.0.tgz", - "integrity": "sha512-HuhP1AlKgvBBxUIwxL/2DsDemiuwgbz1APUNSeJhDBF6JyZuxR0NU8zEZkvH9b4ukTcmcKGABpY0Wex4rAh3xw==", - "dependencies": { - "@aws-sdk/client-sts": "3.521.0", - "@aws-sdk/credential-provider-env": "3.521.0", - "@aws-sdk/credential-provider-process": "3.521.0", - "@aws-sdk/credential-provider-sso": "3.521.0", - "@aws-sdk/credential-provider-web-identity": "3.521.0", - "@aws-sdk/types": "3.521.0", - "@smithy/credential-provider-imds": "^2.2.1", - "@smithy/property-provider": "^2.1.1", - "@smithy/shared-ini-file-loader": "^2.3.1", - "@smithy/types": "^2.10.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.521.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.521.0.tgz", - "integrity": "sha512-N9SR4gWI10qh4V2myBcTw8IlX3QpsMMxa4Q8d/FHiAX6eNV7e6irXkXX8o7+J1gtCRy1AtBMqAdGsve4GVqYMQ==", - "dependencies": { - "@aws-sdk/credential-provider-env": "3.521.0", - "@aws-sdk/credential-provider-http": "3.521.0", - "@aws-sdk/credential-provider-ini": "3.521.0", - "@aws-sdk/credential-provider-process": "3.521.0", - "@aws-sdk/credential-provider-sso": "3.521.0", - "@aws-sdk/credential-provider-web-identity": "3.521.0", - "@aws-sdk/types": "3.521.0", - "@smithy/credential-provider-imds": "^2.2.1", - "@smithy/property-provider": "^2.1.1", - "@smithy/shared-ini-file-loader": "^2.3.1", - "@smithy/types": "^2.10.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.521.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.521.0.tgz", - "integrity": "sha512-EcJjcrpdklxbRAFFgSLk6QGVtvnfZ80ItfZ47VL9LkhWcDAkQ1Oi0esHq+zOgvjb7VkCyD3Q9CyEwT6MlJsriA==", - "dependencies": { - "@aws-sdk/types": "3.521.0", - "@smithy/property-provider": "^2.1.1", - "@smithy/shared-ini-file-loader": "^2.3.1", - "@smithy/types": "^2.10.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.521.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.521.0.tgz", - "integrity": "sha512-GAfc0ji+fC2k9VngYM3zsS1J5ojfWg0WUOBzavvHzkhx/O3CqOt82Vfikg3PvemAp9yOgKPMaasTHVeipNLBBQ==", - "dependencies": { - "@aws-sdk/client-sso": "3.521.0", - "@aws-sdk/token-providers": "3.521.0", - "@aws-sdk/types": "3.521.0", - "@smithy/property-provider": "^2.1.1", - "@smithy/shared-ini-file-loader": "^2.3.1", - "@smithy/types": "^2.10.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.521.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.521.0.tgz", - "integrity": "sha512-ZPPJqdbPOE4BkdrPrYBtsWg0Zy5b+GY1sbMWLQt0tcISgN5EIoePCS2pGNWnBUmBT+mibMQCVv9fOQpqzRkvAw==", - "dependencies": { - "@aws-sdk/client-sts": "3.521.0", - "@aws-sdk/types": "3.521.0", - "@smithy/property-provider": "^2.1.1", - "@smithy/types": "^2.10.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/middleware-host-header": { - "version": "3.521.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.521.0.tgz", - "integrity": "sha512-Bc4stnMtVAdqosYI1wedFK9tffclCuwpOK/JA4bxbnvSyP1kz4s1HBVT9OOMzdLRLWLwVj/RslXKfSbzOUP7ug==", - "dependencies": { - "@aws-sdk/types": "3.521.0", - "@smithy/protocol-http": "^3.2.0", - "@smithy/types": "^2.10.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/middleware-logger": { - "version": "3.521.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.521.0.tgz", - "integrity": "sha512-JJ4nyYvLu3RyyNHo74Rlx6WKxJsAixWCEnnFb6IGRUHvsG+xBGU7HF5koY2log8BqlDLrt4ZUaV/CGy5Dp8Mfg==", - "dependencies": { - "@aws-sdk/types": "3.521.0", - "@smithy/types": "^2.10.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.521.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.521.0.tgz", - "integrity": "sha512-1m5AsC55liTlaYMjc4pIQfjfBHG9LpWgubSl4uUxJSdI++zdA/SRBwXl40p7Ac/y5esweluhWabyiv1g/W4+Xg==", - "dependencies": { - "@aws-sdk/types": "3.521.0", - "@smithy/protocol-http": "^3.2.0", - "@smithy/types": "^2.10.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.521.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.521.0.tgz", - "integrity": "sha512-+hmQjWDG93wCcJn5QY2MkzAL1aG5wl3FJ/ud2nQOu/Gx7d4QVT/B6VJwoG6GSPVuVPZwzne5n9zPVst6RmWJGA==", - "dependencies": { - "@aws-sdk/types": "3.521.0", - "@aws-sdk/util-endpoints": "3.521.0", - "@smithy/protocol-http": "^3.2.0", - "@smithy/types": "^2.10.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/region-config-resolver": { - "version": "3.521.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.521.0.tgz", - "integrity": "sha512-eC2T62nFgQva9Q0Sqoc9xsYyyH9EN2rJtmUKkWsBMf77atpmajAYRl5B/DzLwGHlXGsgVK2tJdU5wnmpQCEwEQ==", - "dependencies": { - "@aws-sdk/types": "3.521.0", - "@smithy/node-config-provider": "^2.2.2", - "@smithy/types": "^2.10.0", - "@smithy/util-config-provider": "^2.2.1", - "@smithy/util-middleware": "^2.1.2", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/token-providers": { - "version": "3.521.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.521.0.tgz", - "integrity": "sha512-63XxPOn13j87yPWKm6UXOPdMZIMyEyCDJzmlxnIACP8m20S/c6b8xLJ4fE/PUlD0MTKxpFeQbandq5OhnLsWSQ==", - "dependencies": { - "@aws-sdk/client-sso-oidc": "3.521.0", - "@aws-sdk/types": "3.521.0", - "@smithy/property-provider": "^2.1.1", - "@smithy/shared-ini-file-loader": "^2.3.1", - "@smithy/types": "^2.10.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/types": { - "version": "3.521.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.521.0.tgz", - "integrity": "sha512-H9I3Lut0F9d+kTibrhnTRqDRzhxf/vrDu12FUdTXVZEvVAQ7w9yrVHAZx8j2e8GWegetsQsNitO3KMrj4dA4pw==", - "dependencies": { - "@smithy/types": "^2.10.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/util-endpoints": { - "version": "3.521.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.521.0.tgz", - "integrity": "sha512-lO5+1LeAZycDqgNjQyZdPSdXFQKXaW5bRuQ3UIT3bOCcUAbDI0BYXlPm1huPNTCEkI9ItnDCbISbV0uF901VXw==", - "dependencies": { - "@aws-sdk/types": "3.521.0", - "@smithy/types": "^2.10.0", - "@smithy/util-endpoints": "^1.1.2", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/util-locate-window": { - "version": "3.495.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.495.0.tgz", - "integrity": "sha512-MfaPXT0kLX2tQaR90saBT9fWQq2DHqSSJRzW+MZWsmF+y5LGCOhO22ac/2o6TKSQm7h0HRc2GaADqYYYor62yg==", - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.521.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.521.0.tgz", - "integrity": "sha512-2t3uW6AXOvJ5iiI1JG9zPqKQDc/TRFa+v13aqT5KKw9h3WHFyRUpd4sFQL6Ul0urrq2Zg9cG4NHBkei3k9lsHA==", - "dependencies": { - "@aws-sdk/types": "3.521.0", - "@smithy/types": "^2.10.0", - "bowser": "^2.11.0", - "tslib": "^2.5.0" - } - }, - "node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.521.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.521.0.tgz", - "integrity": "sha512-g4KMEiyLc8DG21eMrp6fJUdfQ9F0fxfCNMDRgf0SE/pWI/u4vuWR2n8obLwq1pMVx7Ksva1NO3dc+a3Rgr0hag==", - "dependencies": { - "@aws-sdk/types": "3.521.0", - "@smithy/node-config-provider": "^2.2.2", - "@smithy/types": "^2.10.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "aws-crt": ">=1.0.0" - }, - "peerDependenciesMeta": { - "aws-crt": { - "optional": true - } - } - }, - "node_modules/@aws-sdk/util-utf8-browser": { - "version": "3.259.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.259.0.tgz", - "integrity": "sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==", - "dependencies": { - "tslib": "^2.3.1" - } - }, - "node_modules/@azure/abort-controller": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.1.0.tgz", - "integrity": "sha512-TrRLIoSQVzfAJX9H1JeFjzAoDGcoK1IYX1UImfceTZpsyYfWr09Ss1aHW1y5TrrR3iq6RZLBwJ3E24uwPhwahw==", - "dependencies": { - "tslib": "^2.2.0" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/@azure/core-auth": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.6.0.tgz", - "integrity": "sha512-3X9wzaaGgRaBCwhLQZDtFp5uLIXCPrGbwJNWPPugvL4xbIGgScv77YzzxToKGLAKvG9amDoofMoP+9hsH1vs1w==", - "dependencies": { - "@azure/abort-controller": "^2.0.0", - "@azure/core-util": "^1.1.0", - "tslib": "^2.2.0" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@azure/core-auth/node_modules/@azure/abort-controller": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.0.0.tgz", - "integrity": "sha512-RP/mR/WJchR+g+nQFJGOec+nzeN/VvjlwbinccoqfhTsTHbb8X5+mLDp48kHT0ueyum0BNSwGm0kX0UZuIqTGg==", - "dependencies": { - "tslib": "^2.2.0" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@azure/core-client": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.8.0.tgz", - "integrity": "sha512-+gHS3gEzPlhyQBMoqVPOTeNH031R5DM/xpCvz72y38C09rg4Hui/1sJS/ujoisDZbbSHyuRLVWdFlwL0pIFwbg==", - "dependencies": { - "@azure/abort-controller": "^2.0.0", - "@azure/core-auth": "^1.4.0", - "@azure/core-rest-pipeline": "^1.9.1", - "@azure/core-tracing": "^1.0.0", - "@azure/core-util": "^1.0.0", - "@azure/logger": "^1.0.0", - "tslib": "^2.2.0" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@azure/core-client/node_modules/@azure/abort-controller": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.0.0.tgz", - "integrity": "sha512-RP/mR/WJchR+g+nQFJGOec+nzeN/VvjlwbinccoqfhTsTHbb8X5+mLDp48kHT0ueyum0BNSwGm0kX0UZuIqTGg==", - "dependencies": { - "tslib": "^2.2.0" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@azure/core-http-compat": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@azure/core-http-compat/-/core-http-compat-2.0.1.tgz", - "integrity": "sha512-xpQZz/q7E0jSW4rckrTo2mDFDQgo6I69hBU4voMQi7REi6JRW5a+KfVkbJCFCWnkFmP6cAJ0IbuudTdf/MEBOQ==", - "dependencies": { - "@azure/abort-controller": "^1.0.4", - "@azure/core-client": "^1.3.0", - "@azure/core-rest-pipeline": "^1.3.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@azure/core-lro": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@azure/core-lro/-/core-lro-2.6.0.tgz", - "integrity": "sha512-PyRNcaIOfMgoUC01/24NoG+k8O81VrKxYARnDlo+Q2xji0/0/j2nIt8BwQh294pb1c5QnXTDPbNR4KzoDKXEoQ==", - "dependencies": { - "@azure/abort-controller": "^2.0.0", - "@azure/core-util": "^1.2.0", - "@azure/logger": "^1.0.0", - "tslib": "^2.2.0" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@azure/core-lro/node_modules/@azure/abort-controller": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.0.0.tgz", - "integrity": "sha512-RP/mR/WJchR+g+nQFJGOec+nzeN/VvjlwbinccoqfhTsTHbb8X5+mLDp48kHT0ueyum0BNSwGm0kX0UZuIqTGg==", - "dependencies": { - "tslib": "^2.2.0" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@azure/core-paging": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@azure/core-paging/-/core-paging-1.5.0.tgz", - "integrity": "sha512-zqWdVIt+2Z+3wqxEOGzR5hXFZ8MGKK52x4vFLw8n58pR6ZfKRx3EXYTxTaYxYHc/PexPUTyimcTWFJbji9Z6Iw==", - "dependencies": { - "tslib": "^2.2.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@azure/core-rest-pipeline": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.14.0.tgz", - "integrity": "sha512-Tp4M6NsjCmn9L5p7HsW98eSOS7A0ibl3e5ntZglozT0XuD/0y6i36iW829ZbBq0qihlGgfaeFpkLjZ418KDm1Q==", - "dependencies": { - "@azure/abort-controller": "^2.0.0", - "@azure/core-auth": "^1.4.0", - "@azure/core-tracing": "^1.0.1", - "@azure/core-util": "^1.3.0", - "@azure/logger": "^1.0.0", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.0", - "tslib": "^2.2.0" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@azure/core-rest-pipeline/node_modules/@azure/abort-controller": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.0.0.tgz", - "integrity": "sha512-RP/mR/WJchR+g+nQFJGOec+nzeN/VvjlwbinccoqfhTsTHbb8X5+mLDp48kHT0ueyum0BNSwGm0kX0UZuIqTGg==", - "dependencies": { - "tslib": "^2.2.0" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@azure/core-tracing": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.1.tgz", - "integrity": "sha512-I5CGMoLtX+pI17ZdiFJZgxMJApsK6jjfm85hpgp3oazCdq5Wxgh4wMr7ge/TTWW1B5WBuvIOI1fMU/FrOAMKrw==", - "dependencies": { - "tslib": "^2.2.0" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/@azure/core-util": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.7.0.tgz", - "integrity": "sha512-Zq2i3QO6k9DA8vnm29mYM4G8IE9u1mhF1GUabVEqPNX8Lj833gdxQ2NAFxt2BZsfAL+e9cT8SyVN7dFVJ/Hf0g==", - "dependencies": { - "@azure/abort-controller": "^2.0.0", - "tslib": "^2.2.0" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@azure/core-util/node_modules/@azure/abort-controller": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.0.0.tgz", - "integrity": "sha512-RP/mR/WJchR+g+nQFJGOec+nzeN/VvjlwbinccoqfhTsTHbb8X5+mLDp48kHT0ueyum0BNSwGm0kX0UZuIqTGg==", - "dependencies": { - "tslib": "^2.2.0" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@azure/identity": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-4.0.1.tgz", - "integrity": "sha512-yRdgF03SFLqUMZZ1gKWt0cs0fvrDIkq2bJ6Oidqcoo5uM85YMBnXWMzYKK30XqIT76lkFyAaoAAy5knXhrG4Lw==", - "dependencies": { - "@azure/abort-controller": "^1.0.0", - "@azure/core-auth": "^1.5.0", - "@azure/core-client": "^1.4.0", - "@azure/core-rest-pipeline": "^1.1.0", - "@azure/core-tracing": "^1.0.0", - "@azure/core-util": "^1.3.0", - "@azure/logger": "^1.0.0", - "@azure/msal-browser": "^3.5.0", - "@azure/msal-node": "^2.5.1", - "events": "^3.0.0", - "jws": "^4.0.0", - "open": "^8.0.0", - "stoppable": "^1.1.0", - "tslib": "^2.2.0" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@azure/keyvault-secrets": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/@azure/keyvault-secrets/-/keyvault-secrets-4.8.0.tgz", - "integrity": "sha512-RGfpFk6XUXHfWuTAiokOe8t6ej5C4ijf4HVyJUmTfN6VjDBVPvTtoiOi/C5072/ENHScYZFhiYOgIjLgYjfJ/A==", - "dependencies": { - "@azure/abort-controller": "^1.0.0", - "@azure/core-auth": "^1.3.0", - "@azure/core-client": "^1.5.0", - "@azure/core-http-compat": "^2.0.1", - "@azure/core-lro": "^2.2.0", - "@azure/core-paging": "^1.1.1", - "@azure/core-rest-pipeline": "^1.8.0", - "@azure/core-tracing": "^1.0.0", - "@azure/core-util": "^1.0.0", - "@azure/logger": "^1.0.0", - "tslib": "^2.2.0" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@azure/logger": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.0.4.tgz", - "integrity": "sha512-ustrPY8MryhloQj7OWGe+HrYx+aoiOxzbXTtgblbV3xwCqpzUK36phH3XNHQKj3EPonyFUuDTfR3qFhTEAuZEg==", - "dependencies": { - "tslib": "^2.2.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@azure/msal-browser": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-3.10.0.tgz", - "integrity": "sha512-mnmi8dCXVNZI+AGRq0jKQ3YiodlIC4W9npr6FCB9WN6NQT+6rq+cIlxgUb//BjLyzKsnYo+i4LROGeMyU+6v1A==", - "dependencies": { - "@azure/msal-common": "14.7.1" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@azure/msal-common": { - "version": "14.7.1", - "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-14.7.1.tgz", - "integrity": "sha512-v96btzjM7KrAu4NSEdOkhQSTGOuNUIIsUdB8wlyB9cdgl5KqEKnTonHUZ8+khvZ6Ap542FCErbnTyDWl8lZ2rA==", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@azure/msal-node": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-2.6.4.tgz", - "integrity": "sha512-nNvEPx009/80UATCToF+29NZYocn01uKrB91xtFr7bSqkqO1PuQGXRyYwryWRztUrYZ1YsSbw9A+LmwOhpVvcg==", - "dependencies": { - "@azure/msal-common": "14.7.1", - "jsonwebtoken": "^9.0.0", - "uuid": "^8.3.0" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/@azure/msal-node/node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", - "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.9.tgz", - "integrity": "sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA==", - "dev": true, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/runtime": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.9.tgz", - "integrity": "sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.9.tgz", - "integrity": "sha512-dQjSq/7HaSjRM43FFGnv5keM2HsxpmyV1PfaSVm0nzzjwwTmjOe6J4bC8e3+pTEIgHaHj+1ZlLThRJ2auc/w1Q==", - "dev": true, - "dependencies": { - "@babel/helper-string-parser": "^7.23.4", - "@babel/helper-validator-identifier": "^7.22.20", - "to-fast-properties": "^2.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@balena/dockerignore": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@balena/dockerignore/-/dockerignore-1.0.2.tgz", - "integrity": "sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q==", - "dev": true - }, - "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/@docsearch/css": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.5.2.tgz", - "integrity": "sha512-SPiDHaWKQZpwR2siD0KQUwlStvIAnEyK6tAE2h2Wuoq8ue9skzhlyVQ1ddzOxX6khULnAALDiR/isSF3bnuciA==", - "dev": true - }, - "node_modules/@docsearch/js": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/@docsearch/js/-/js-3.5.2.tgz", - "integrity": "sha512-p1YFTCDflk8ieHgFJYfmyHBki1D61+U9idwrLh+GQQMrBSP3DLGKpy0XUJtPjAOPltcVbqsTjiPFfH7JImjUNg==", - "dev": true, - "dependencies": { - "@docsearch/react": "3.5.2", - "preact": "^10.0.0" - } - }, - "node_modules/@docsearch/react": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.5.2.tgz", - "integrity": "sha512-9Ahcrs5z2jq/DcAvYtvlqEBHImbm4YJI8M9y0x6Tqg598P40HTEkX7hsMcIuThI+hTFxRGZ9hll0Wygm2yEjng==", - "dev": true, - "dependencies": { - "@algolia/autocomplete-core": "1.9.3", - "@algolia/autocomplete-preset-algolia": "1.9.3", - "@docsearch/css": "3.5.2", - "algoliasearch": "^4.19.1" - }, - "peerDependencies": { - "@types/react": ">= 16.8.0 < 19.0.0", - "react": ">= 16.8.0 < 19.0.0", - "react-dom": ">= 16.8.0 < 19.0.0", - "search-insights": ">= 1 < 3" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "react": { - "optional": true - }, - "react-dom": { - "optional": true - }, - "search-insights": { - "optional": true - } - } - }, - "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/@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/@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/@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/@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/@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/@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/@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/@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/@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/@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/@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/@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/@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/@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/@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/@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/@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/@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/@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/@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/@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/@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/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", - "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", - "dev": true, - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", - "dev": true, - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@eslint/js": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", - "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/@fastify/accept-negotiator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@fastify/accept-negotiator/-/accept-negotiator-1.1.0.tgz", - "integrity": "sha512-OIHZrb2ImZ7XG85HXOONLcJWGosv7sIvM2ifAPQVhg9Lv7qdmMBNVaai4QTdyuaqbKM5eO6sLSQOYI7wEQeCJQ==", - "engines": { - "node": ">=14" - } - }, - "node_modules/@fastify/ajv-compiler": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/@fastify/ajv-compiler/-/ajv-compiler-3.5.0.tgz", - "integrity": "sha512-ebbEtlI7dxXF5ziNdr05mOY8NnDiPB1XvAlLHctRt/Rc+C3LCOVW5imUVX+mhvUhnNzmPBHewUkOFgGlCxgdAA==", - "dependencies": { - "ajv": "^8.11.0", - "ajv-formats": "^2.1.1", - "fast-uri": "^2.0.0" - } - }, - "node_modules/@fastify/ajv-compiler/node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/@fastify/ajv-compiler/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, - "node_modules/@fastify/compress": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@fastify/compress/-/compress-7.0.0.tgz", - "integrity": "sha512-jo/NaBVHP1OXIf8Kmr3bZyYQB0gAIgcy5c8rRKTPjhklHO7lRs/6ZFckUVT0NtbKSvrTuIcmSkxYpjyv3FNHXA==", - "dependencies": { - "@fastify/accept-negotiator": "^1.1.0", - "fastify-plugin": "^4.5.0", - "into-stream": "^6.0.0", - "mime-db": "^1.52.0", - "minipass": "^7.0.2", - "peek-stream": "^1.1.3", - "pump": "^3.0.0", - "pumpify": "^2.0.1" - } - }, - "node_modules/@fastify/cors": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/@fastify/cors/-/cors-9.0.1.tgz", - "integrity": "sha512-YY9Ho3ovI+QHIL2hW+9X4XqQjXLjJqsU+sMV/xFsxZkE8p3GNnYVFpoOxF7SsP5ZL76gwvbo3V9L+FIekBGU4Q==", - "dependencies": { - "fastify-plugin": "^4.0.0", - "mnemonist": "0.39.6" - } - }, - "node_modules/@fastify/error": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/@fastify/error/-/error-3.4.1.tgz", - "integrity": "sha512-wWSvph+29GR783IhmvdwWnN4bUxTD01Vm5Xad4i7i1VuAOItLvbPAb69sb0IQ2N57yprvhNIwAP5B6xfKTmjmQ==" - }, - "node_modules/@fastify/fast-json-stringify-compiler": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@fastify/fast-json-stringify-compiler/-/fast-json-stringify-compiler-4.3.0.tgz", - "integrity": "sha512-aZAXGYo6m22Fk1zZzEUKBvut/CIIQe/BapEORnxiD5Qr0kPHqqI69NtEMCme74h+at72sPhbkb4ZrLd1W3KRLA==", - "dependencies": { - "fast-json-stringify": "^5.7.0" - } - }, - "node_modules/@fastify/helmet": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/@fastify/helmet/-/helmet-11.1.1.tgz", - "integrity": "sha512-pjJxjk6SLEimITWadtYIXt6wBMfFC1I6OQyH/jYVCqSAn36sgAIFjeNiibHtifjCd+e25442pObis3Rjtame6A==", - "dependencies": { - "fastify-plugin": "^4.2.1", - "helmet": "^7.0.0" - } - }, - "node_modules/@fastify/merge-json-schemas": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@fastify/merge-json-schemas/-/merge-json-schemas-0.1.1.tgz", - "integrity": "sha512-fERDVz7topgNjtXsJTTW1JKLy0rhuLRcquYqNR9rF7OcVpCa2OVW49ZPDIhaRRCaUuvVxI+N416xUoF76HNSXA==", - "dependencies": { - "fast-deep-equal": "^3.1.3" - } - }, - "node_modules/@fastify/send": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@fastify/send/-/send-2.1.0.tgz", - "integrity": "sha512-yNYiY6sDkexoJR0D8IDy3aRP3+L4wdqCpvx5WP+VtEU58sn7USmKynBzDQex5X42Zzvw2gNzzYgP90UfWShLFA==", - "dependencies": { - "@lukeed/ms": "^2.0.1", - "escape-html": "~1.0.3", - "fast-decode-uri-component": "^1.0.1", - "http-errors": "2.0.0", - "mime": "^3.0.0" - } - }, - "node_modules/@fastify/static": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@fastify/static/-/static-7.0.1.tgz", - "integrity": "sha512-i1p/nELMknAisNfnjo7yhfoUOdKzA+n92QaMirv2NkZrJ1Wl12v2nyTYlDwPN8XoStMBAnRK/Kx6zKmfrXUPXw==", - "dependencies": { - "@fastify/accept-negotiator": "^1.0.0", - "@fastify/send": "^2.0.0", - "content-disposition": "^0.5.3", - "fastify-plugin": "^4.0.0", - "fastq": "^1.17.0", - "glob": "^10.3.4" - } - }, - "node_modules/@google-cloud/secret-manager": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@google-cloud/secret-manager/-/secret-manager-5.1.0.tgz", - "integrity": "sha512-4DYSrsdmJthJIoklqih43bXBjC+quCkpwXJrvtBQw6S5x3XvcMq/uLnKWWZSpxSzcLiKgi/95EQ4gL77AQZ2Bg==", - "dependencies": { - "google-gax": "^4.0.3" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@grpc/grpc-js": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.7.3.tgz", - "integrity": "sha512-H9l79u4kJ2PVSxUNA08HMYAnUBLj9v6KjYQ7SQ71hOZcEXhShE/y5iQCesP8+6/Ik/7i2O0a10bPquIcYfufog==", - "dependencies": { - "@grpc/proto-loader": "^0.7.0", - "@types/node": ">=12.12.47" - }, - "engines": { - "node": "^8.13.0 || >=10.10.0" - } - }, - "node_modules/@grpc/proto-loader": { - "version": "0.7.10", - "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.10.tgz", - "integrity": "sha512-CAqDfoaQ8ykFd9zqBDn4k6iWT9loLAlc2ETmDFS9JCD70gDcnA4L3AFEo2iV7KyAtAAHFW9ftq1Fz+Vsgq80RQ==", - "dependencies": { - "lodash.camelcase": "^4.3.0", - "long": "^5.0.0", - "protobufjs": "^7.2.4", - "yargs": "^17.7.2" - }, - "bin": { - "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@hapi/b64": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@hapi/b64/-/b64-5.0.0.tgz", - "integrity": "sha512-ngu0tSEmrezoiIaNGG6rRvKOUkUuDdf4XTPnONHGYfSGRmDqPZX5oJL6HAdKTo1UQHECbdB4OzhWrfgVppjHUw==", - "dependencies": { - "@hapi/hoek": "9.x.x" - } - }, - "node_modules/@hapi/boom": { - "version": "9.1.4", - "resolved": "https://registry.npmjs.org/@hapi/boom/-/boom-9.1.4.tgz", - "integrity": "sha512-Ls1oH8jaN1vNsqcaHVYJrKmgMcKsC1wcp8bujvXrHaAqD2iDYq3HoOwsxwo09Cuda5R5nC0o0IxlrlTuvPuzSw==", - "dependencies": { - "@hapi/hoek": "9.x.x" - } - }, - "node_modules/@hapi/bourne": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@hapi/bourne/-/bourne-2.1.0.tgz", - "integrity": "sha512-i1BpaNDVLJdRBEKeJWkVO6tYX6DMFBuwMhSuWqLsY4ufeTKGVuV5rBsUhxPayXqnnWHgXUAmWK16H/ykO5Wj4Q==" - }, - "node_modules/@hapi/cryptiles": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@hapi/cryptiles/-/cryptiles-5.1.0.tgz", - "integrity": "sha512-fo9+d1Ba5/FIoMySfMqPBR/7Pa29J2RsiPrl7bkwo5W5o+AN1dAYQRi4SPrPwwVxVGKjgLOEWrsvt1BonJSfLA==", - "dependencies": { - "@hapi/boom": "9.x.x" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/@hapi/hoek": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", - "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" - }, - "node_modules/@hapi/iron": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@hapi/iron/-/iron-6.0.0.tgz", - "integrity": "sha512-zvGvWDufiTGpTJPG1Y/McN8UqWBu0k/xs/7l++HVU535NLHXsHhy54cfEMdW7EjwKfbBfM9Xy25FmTiobb7Hvw==", - "dependencies": { - "@hapi/b64": "5.x.x", - "@hapi/boom": "9.x.x", - "@hapi/bourne": "2.x.x", - "@hapi/cryptiles": "5.x.x", - "@hapi/hoek": "9.x.x" - } - }, - "node_modules/@hapi/podium": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/@hapi/podium/-/podium-4.1.3.tgz", - "integrity": "sha512-ljsKGQzLkFqnQxE7qeanvgGj4dejnciErYd30dbrYzUOF/FyS/DOF97qcrT3bhoVwCYmxa6PEMhxfCPlnUcD2g==", - "dependencies": { - "@hapi/hoek": "9.x.x", - "@hapi/teamwork": "5.x.x", - "@hapi/validate": "1.x.x" - } - }, - "node_modules/@hapi/teamwork": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@hapi/teamwork/-/teamwork-5.1.1.tgz", - "integrity": "sha512-1oPx9AE5TIv+V6Ih54RP9lTZBso3rP8j4Xhb6iSVwPXtAM+sDopl5TFMv5Paw73UnpZJ9gjcrTE1BXrWt9eQrg==", - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/@hapi/topo": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", - "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", - "dependencies": { - "@hapi/hoek": "^9.0.0" - } - }, - "node_modules/@hapi/validate": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@hapi/validate/-/validate-1.1.3.tgz", - "integrity": "sha512-/XMR0N0wjw0Twzq2pQOzPBZlDzkekGcoCtzO314BpIEsbXdYGthQUbxgkGDf4nhk1+IPDAsXqWjMohRQYO06UA==", - "dependencies": { - "@hapi/hoek": "^9.0.0", - "@hapi/topo": "^5.0.0" - } - }, - "node_modules/@hono/node-server": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.8.1.tgz", - "integrity": "sha512-9ondWs/5EuyZOd/iyiRp0GQfMEq+IA+IbV8Wy2bgdr5+cC55I3xbQrqtXKprw7jJhfvq3IVw7ALiuvCEmLdCAA==", - "engines": { - "node": ">=18.14.1" - } - }, - "node_modules/@hono/swagger-ui": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@hono/swagger-ui/-/swagger-ui-0.2.1.tgz", - "integrity": "sha512-wBxVMRe3/v8xH4o6icmwztiIq0DG0s7+jHVMHVUAoFFCWEQNL2iskMmQtrhSDtsFmBZUeUFQUaaJ6Ir6DOmHLA==", - "peerDependencies": { - "hono": "*" - } - }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.11.14", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", - "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", - "dev": true, - "dependencies": { - "@humanwhocodes/object-schema": "^2.0.2", - "debug": "^4.3.1", - "minimatch": "^3.0.5" - }, - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", - "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", - "dev": true - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "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/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", - "dev": true, - "dependencies": { - "@sinclair/typebox": "^0.27.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.4.tgz", - "integrity": "sha512-Oud2QPM5dHviZNn4y/WhhYKSXksv+1xLEIsNrAbGcFzUN3ubqWRFT5gwPchNc5NuzILOU4tPBDTZ4VwhL8Y7cw==", - "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "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==", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/source-map": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", - "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.23", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.23.tgz", - "integrity": "sha512-9/4foRoUKp8s96tSkh8DlAAc5A0Ty8vLXld+l9gjKKY6ckwI8G15f0hskGmuLZu78ZlGa1vtsfOa+lnB4vG6Jg==", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@ljharb/through": { - "version": "2.3.12", - "resolved": "https://registry.npmjs.org/@ljharb/through/-/through-2.3.12.tgz", - "integrity": "sha512-ajo/heTlG3QgC8EGP6APIejksVAYt4ayz4tqoP3MolFELzcH1x1fzwEYRJTPO0IELutZ5HQ0c26/GqAYy79u3g==", - "dependencies": { - "call-bind": "^1.0.5" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/@lukeed/ms": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@lukeed/ms/-/ms-2.0.2.tgz", - "integrity": "sha512-9I2Zn6+NJLfaGoz9jN3lpwDgAYvfGeNYdbAIjJOqzs4Tpc+VU3Jqq4IofSUBKajiDS8k9fZIg18/z13mpk1bsA==", - "engines": { - "node": ">=8" - } - }, - "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==", - "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==", - "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==", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@opentelemetry/api": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.7.0.tgz", - "integrity": "sha512-AdY5wvN0P2vXBi3b29hxZgSFvdhdxPB9+f0B6s//P9Q8nibRWeA3cHm8UmLpio9ABigkVHJ5NMPk+Mz8VCCyrw==", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/api-logs": { - "version": "0.48.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.48.0.tgz", - "integrity": "sha512-1/aMiU4Eqo3Zzpfwu51uXssp5pzvHFObk8S9pKAiXb1ne8pvg1qxBQitYL1XUiAMEXFzgjaidYG2V6624DRhhw==", - "dependencies": { - "@opentelemetry/api": "^1.0.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/@opentelemetry/auto-instrumentations-node": { - "version": "0.40.3", - "resolved": "https://registry.npmjs.org/@opentelemetry/auto-instrumentations-node/-/auto-instrumentations-node-0.40.3.tgz", - "integrity": "sha512-MgBCzpFU4FBQEsXPgt5driYzxErf2JGntQ8Amtc94sqrnvq+FKdEBa6vxOpZlPM+c4Xr6tPpAT1ecTBV8U87hw==", - "dependencies": { - "@opentelemetry/instrumentation": "^0.46.0", - "@opentelemetry/instrumentation-amqplib": "^0.33.5", - "@opentelemetry/instrumentation-aws-lambda": "^0.37.4", - "@opentelemetry/instrumentation-aws-sdk": "^0.37.2", - "@opentelemetry/instrumentation-bunyan": "^0.34.1", - "@opentelemetry/instrumentation-cassandra-driver": "^0.34.2", - "@opentelemetry/instrumentation-connect": "^0.32.4", - "@opentelemetry/instrumentation-cucumber": "^0.2.1", - "@opentelemetry/instrumentation-dataloader": "^0.5.4", - "@opentelemetry/instrumentation-dns": "^0.32.5", - "@opentelemetry/instrumentation-express": "^0.34.1", - "@opentelemetry/instrumentation-fastify": "^0.32.6", - "@opentelemetry/instrumentation-fs": "^0.8.4", - "@opentelemetry/instrumentation-generic-pool": "^0.32.5", - "@opentelemetry/instrumentation-graphql": "^0.36.1", - "@opentelemetry/instrumentation-grpc": "^0.46.0", - "@opentelemetry/instrumentation-hapi": "^0.33.3", - "@opentelemetry/instrumentation-http": "^0.46.0", - "@opentelemetry/instrumentation-ioredis": "^0.36.1", - "@opentelemetry/instrumentation-knex": "^0.32.4", - "@opentelemetry/instrumentation-koa": "^0.36.4", - "@opentelemetry/instrumentation-lru-memoizer": "^0.33.5", - "@opentelemetry/instrumentation-memcached": "^0.32.5", - "@opentelemetry/instrumentation-mongodb": "^0.38.1", - "@opentelemetry/instrumentation-mongoose": "^0.34.0", - "@opentelemetry/instrumentation-mysql": "^0.34.5", - "@opentelemetry/instrumentation-mysql2": "^0.34.5", - "@opentelemetry/instrumentation-nestjs-core": "^0.33.4", - "@opentelemetry/instrumentation-net": "^0.32.5", - "@opentelemetry/instrumentation-pg": "^0.37.2", - "@opentelemetry/instrumentation-pino": "^0.34.5", - "@opentelemetry/instrumentation-redis": "^0.35.5", - "@opentelemetry/instrumentation-redis-4": "^0.35.6", - "@opentelemetry/instrumentation-restify": "^0.34.3", - "@opentelemetry/instrumentation-router": "^0.33.4", - "@opentelemetry/instrumentation-socket.io": "^0.35.0", - "@opentelemetry/instrumentation-tedious": "^0.6.5", - "@opentelemetry/instrumentation-winston": "^0.33.1", - "@opentelemetry/resource-detector-alibaba-cloud": "^0.28.5", - "@opentelemetry/resource-detector-aws": "^1.3.5", - "@opentelemetry/resource-detector-container": "^0.3.5", - "@opentelemetry/resource-detector-gcp": "^0.29.5", - "@opentelemetry/resources": "^1.12.0", - "@opentelemetry/sdk-node": "^0.46.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.4.1" - } - }, - "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/api-logs": { - "version": "0.46.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.46.0.tgz", - "integrity": "sha512-+9BcqfiEDGPXEIo+o3tso/aqGM5dGbGwAkGVp3FPpZ8GlkK1YlaKRd9gMVyPaeRATwvO5wYGGnCsAc/sMMM9Qw==", - "dependencies": { - "@opentelemetry/api": "^1.0.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/context-async-hooks": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.19.0.tgz", - "integrity": "sha512-0i1ECOc9daKK3rjUgDDXf0GDD5XfCou5lXnt2DALIc2qKoruPPcesobNKE54laSVUWnC3jX26RzuOa31g0V32A==", - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.8.0" - } - }, - "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/core": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.19.0.tgz", - "integrity": "sha512-w42AukJh3TP8R0IZZOVJVM/kMWu8g+lm4LzT70WtuKqhwq7KVhcDzZZuZinWZa6TtQCl7Smt2wolEYzpHabOgw==", - "dependencies": { - "@opentelemetry/semantic-conventions": "1.19.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.8.0" - } - }, - "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/exporter-trace-otlp-grpc": { - "version": "0.46.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-grpc/-/exporter-trace-otlp-grpc-0.46.0.tgz", - "integrity": "sha512-kR4kehnfIhv7v/2MuNYfrlh9A/ZtQofwCzurTIplornUjdzhKDGgjui1NkNTqTfM1QkqfCiavGsf5hwocx29bA==", - "dependencies": { - "@grpc/grpc-js": "^1.7.1", - "@opentelemetry/core": "1.19.0", - "@opentelemetry/otlp-grpc-exporter-base": "0.46.0", - "@opentelemetry/otlp-transformer": "0.46.0", - "@opentelemetry/resources": "1.19.0", - "@opentelemetry/sdk-trace-base": "1.19.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.0" - } - }, - "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/exporter-trace-otlp-http": { - "version": "0.46.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.46.0.tgz", - "integrity": "sha512-vZ2pYOB+qrQ+jnKPY6Gnd58y1k/Ti//Ny6/XsSX7/jED0X77crtSVgC6N5UA0JiGJOh6QB2KE9gaH99010XHzg==", - "dependencies": { - "@opentelemetry/core": "1.19.0", - "@opentelemetry/otlp-exporter-base": "0.46.0", - "@opentelemetry/otlp-transformer": "0.46.0", - "@opentelemetry/resources": "1.19.0", - "@opentelemetry/sdk-trace-base": "1.19.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.0" - } - }, - "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/exporter-trace-otlp-proto": { - "version": "0.46.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-proto/-/exporter-trace-otlp-proto-0.46.0.tgz", - "integrity": "sha512-A7PftDM57w1TLiirrhi8ceAnCpYkpUBObELdn239IyYF67zwngImGfBLf5Yo3TTAOA2Oj1TL76L8zWVL8W+Suw==", - "dependencies": { - "@opentelemetry/core": "1.19.0", - "@opentelemetry/otlp-exporter-base": "0.46.0", - "@opentelemetry/otlp-proto-exporter-base": "0.46.0", - "@opentelemetry/otlp-transformer": "0.46.0", - "@opentelemetry/resources": "1.19.0", - "@opentelemetry/sdk-trace-base": "1.19.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.0" - } - }, - "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/exporter-zipkin": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-zipkin/-/exporter-zipkin-1.19.0.tgz", - "integrity": "sha512-TY1fy4JiOBN5a8T9fknqTMcz0DXIeFBr6sklaLCgwtj+G699a5R4CekNwpeM7DHSwC44UMX7gljO2I6dYsTS3A==", - "dependencies": { - "@opentelemetry/core": "1.19.0", - "@opentelemetry/resources": "1.19.0", - "@opentelemetry/sdk-trace-base": "1.19.0", - "@opentelemetry/semantic-conventions": "1.19.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.0" - } - }, - "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/otlp-exporter-base": { - "version": "0.46.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.46.0.tgz", - "integrity": "sha512-hfkh7cG17l77ZSLRAogz19SIJzr0KeC7xv5PDyTFbHFpwwoxV/bEViO49CqUFH6ckXB63NrltASP9R7po+ahTQ==", - "dependencies": { - "@opentelemetry/core": "1.19.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.0" - } - }, - "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/otlp-grpc-exporter-base": { - "version": "0.46.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-grpc-exporter-base/-/otlp-grpc-exporter-base-0.46.0.tgz", - "integrity": "sha512-/KB/xfZZiWIY2JknvCoT/e9paIzQO3QCBN5gR6RyxpXM/AGx3YTAOKvB/Ts9Va19jo5aE74gB7emhFaCNy4Rmw==", - "dependencies": { - "@grpc/grpc-js": "^1.7.1", - "@opentelemetry/core": "1.19.0", - "@opentelemetry/otlp-exporter-base": "0.46.0", - "protobufjs": "^7.2.3" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.0" - } - }, - "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/otlp-proto-exporter-base": { - "version": "0.46.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-proto-exporter-base/-/otlp-proto-exporter-base-0.46.0.tgz", - "integrity": "sha512-rEJBA8U2AxfEzrdIUcyyjOweyVFkO6V1XAxwP161JkxpvNuVDdULHAfRVnGtoZhiVA1XsJKcpIIq2MEKAqq4cg==", - "dependencies": { - "@opentelemetry/core": "1.19.0", - "@opentelemetry/otlp-exporter-base": "0.46.0", - "protobufjs": "^7.2.3" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.0" - } - }, - "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/otlp-transformer": { - "version": "0.46.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.46.0.tgz", - "integrity": "sha512-Fj9hZwr6xuqgsaERn667Uf6kuDG884puWhyrai2Jen2Fq+bGf4/5BzEJp/8xvty0VSU4EfXOto/ys3KpSz2UHg==", - "dependencies": { - "@opentelemetry/api-logs": "0.46.0", - "@opentelemetry/core": "1.19.0", - "@opentelemetry/resources": "1.19.0", - "@opentelemetry/sdk-logs": "0.46.0", - "@opentelemetry/sdk-metrics": "1.19.0", - "@opentelemetry/sdk-trace-base": "1.19.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.3.0 <1.8.0" - } - }, - "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/propagator-b3": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-1.19.0.tgz", - "integrity": "sha512-v7y5IBOKBm0vP3yf0DHzlw4L2gL6tZ0KeeMTaxfO5IuomMffDbrGWcvYFp0Dt4LdZctTSK523rVLBB9FBHBciQ==", - "dependencies": { - "@opentelemetry/core": "1.19.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.8.0" - } - }, - "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/propagator-jaeger": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-1.19.0.tgz", - "integrity": "sha512-dedkOoTzKg+nYoLWCMp0Im+wo+XkTRW6aXhi8VQRtMW/9SNJGOllCJSu8llToLxMDF0+6zu7OCrKkevAof2tew==", - "dependencies": { - "@opentelemetry/core": "1.19.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.8.0" - } - }, - "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/resources": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.19.0.tgz", - "integrity": "sha512-RgxvKuuMOf7nctOeOvpDjt2BpZvZGr9Y0vf7eGtY5XYZPkh2p7e2qub1S2IArdBMf9kEbz0SfycqCviOu9isqg==", - "dependencies": { - "@opentelemetry/core": "1.19.0", - "@opentelemetry/semantic-conventions": "1.19.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.8.0" - } - }, - "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/sdk-logs": { - "version": "0.46.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.46.0.tgz", - "integrity": "sha512-Knlyk4+G72uEzNh6GRN1Fhmrj+/rkATI5/lOrevN7zRDLgp4kfyZBGGoWk7w+qQjlYvwhIIdPVxlIcipivdZIg==", - "dependencies": { - "@opentelemetry/core": "1.19.0", - "@opentelemetry/resources": "1.19.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.4.0 <1.8.0", - "@opentelemetry/api-logs": ">=0.39.1" - } - }, - "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/sdk-metrics": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.19.0.tgz", - "integrity": "sha512-FiMii40zr0Fmys4F1i8gmuCvbinBnBsDeGBr4FQemOf0iPCLytYQm5AZJ/nn4xSc71IgKBQwTFQRAGJI7JvZ4Q==", - "dependencies": { - "@opentelemetry/core": "1.19.0", - "@opentelemetry/resources": "1.19.0", - "lodash.merge": "^4.6.2" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.3.0 <1.8.0" - } - }, - "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/sdk-node": { - "version": "0.46.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-node/-/sdk-node-0.46.0.tgz", - "integrity": "sha512-BQhzdCRZXchhKjZaFkgxlgoowjOt/QXekJ1CZgfvFO9Yg5GV15LyJFUEyQkDyD8XbshGo3Cnj0WZMBnDWtWY1A==", - "dependencies": { - "@opentelemetry/api-logs": "0.46.0", - "@opentelemetry/core": "1.19.0", - "@opentelemetry/exporter-trace-otlp-grpc": "0.46.0", - "@opentelemetry/exporter-trace-otlp-http": "0.46.0", - "@opentelemetry/exporter-trace-otlp-proto": "0.46.0", - "@opentelemetry/exporter-zipkin": "1.19.0", - "@opentelemetry/instrumentation": "0.46.0", - "@opentelemetry/resources": "1.19.0", - "@opentelemetry/sdk-logs": "0.46.0", - "@opentelemetry/sdk-metrics": "1.19.0", - "@opentelemetry/sdk-trace-base": "1.19.0", - "@opentelemetry/sdk-trace-node": "1.19.0", - "@opentelemetry/semantic-conventions": "1.19.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.3.0 <1.8.0" - } - }, - "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/sdk-trace-base": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.19.0.tgz", - "integrity": "sha512-+IRvUm+huJn2KqfFW3yW/cjvRwJ8Q7FzYHoUNx5Fr0Lws0LxjMJG1uVB8HDpLwm7mg5XXH2M5MF+0jj5cM8BpQ==", - "dependencies": { - "@opentelemetry/core": "1.19.0", - "@opentelemetry/resources": "1.19.0", - "@opentelemetry/semantic-conventions": "1.19.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.8.0" - } - }, - "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/sdk-trace-node": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-1.19.0.tgz", - "integrity": "sha512-TCiEq/cUjM15RFqBRwWomTVbOqzndWL4ILa7ZCu0zbjU1/XY6AgHkgrgAc7vGP6TjRqH4Xryuglol8tcIfbBUQ==", - "dependencies": { - "@opentelemetry/context-async-hooks": "1.19.0", - "@opentelemetry/core": "1.19.0", - "@opentelemetry/propagator-b3": "1.19.0", - "@opentelemetry/propagator-jaeger": "1.19.0", - "@opentelemetry/sdk-trace-base": "1.19.0", - "semver": "^7.5.2" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.8.0" - } - }, - "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/semantic-conventions": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.19.0.tgz", - "integrity": "sha512-14jRpC8f5c0gPSwoZ7SbEJni1PqI+AhAE8m1bMz6v+RPM4OlP1PT2UHBJj5Qh/ALLPjhVU/aZUK3YyjTUqqQVg==", - "engines": { - "node": ">=14" - } - }, - "node_modules/@opentelemetry/context-async-hooks": { - "version": "1.21.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.21.0.tgz", - "integrity": "sha512-t0iulGPiMjG/NrSjinPQoIf8ST/o9V0dGOJthfrFporJlNdlKIQPfC7lkrV+5s2dyBThfmSbJlp/4hO1eOcDXA==", - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.8.0" - } - }, - "node_modules/@opentelemetry/core": { - "version": "1.21.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.21.0.tgz", - "integrity": "sha512-KP+OIweb3wYoP7qTYL/j5IpOlu52uxBv5M4+QhSmmUfLyTgu1OIS71msK3chFo1D6Y61BIH3wMiMYRCxJCQctA==", - "dependencies": { - "@opentelemetry/semantic-conventions": "1.21.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.8.0" - } - }, - "node_modules/@opentelemetry/exporter-logs-otlp-http": { - "version": "0.48.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-http/-/exporter-logs-otlp-http-0.48.0.tgz", - "integrity": "sha512-Glxl0ZmyHqykGjcv5T4HMvw4fQVZoiCV0oMolPgXBBnuTuOHS/dEdoGX6hNp4Xqw55YALM/CJocHwkRqBQnPgw==", - "dependencies": { - "@opentelemetry/api-logs": "0.48.0", - "@opentelemetry/core": "1.21.0", - "@opentelemetry/otlp-exporter-base": "0.48.0", - "@opentelemetry/otlp-transformer": "0.48.0", - "@opentelemetry/sdk-logs": "0.48.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.0" - } - }, - "node_modules/@opentelemetry/exporter-metrics-otlp-http": { - "version": "0.48.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-http/-/exporter-metrics-otlp-http-0.48.0.tgz", - "integrity": "sha512-lZ0gah/WjPpUBVR2Qml8GHraLznsXEpmIS897vdL2IXCxJzGev7sCb9IwAiq89+MgHkuGUWhTWFB2frKrqX1sA==", - "dependencies": { - "@opentelemetry/core": "1.21.0", - "@opentelemetry/otlp-exporter-base": "0.48.0", - "@opentelemetry/otlp-transformer": "0.48.0", - "@opentelemetry/resources": "1.21.0", - "@opentelemetry/sdk-metrics": "1.21.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/exporter-trace-otlp-grpc": { - "version": "0.48.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-grpc/-/exporter-trace-otlp-grpc-0.48.0.tgz", - "integrity": "sha512-+qRQXUbdRW6aNRT5yWOG3G6My1VxxKeqgUyLkkdIjkT20lvymjiN2RpBfGMtAf/oqnuRknf9snFl9VSIO2gniw==", - "dependencies": { - "@grpc/grpc-js": "^1.7.1", - "@opentelemetry/core": "1.21.0", - "@opentelemetry/otlp-grpc-exporter-base": "0.48.0", - "@opentelemetry/otlp-transformer": "0.48.0", - "@opentelemetry/resources": "1.21.0", - "@opentelemetry/sdk-trace-base": "1.21.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.0" - } - }, - "node_modules/@opentelemetry/exporter-trace-otlp-http": { - "version": "0.48.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.48.0.tgz", - "integrity": "sha512-QEZKbfWqXrbKVpr2PHd4KyKI0XVOhUYC+p2RPV8s+2K5QzZBE3+F9WlxxrXDfkrvGmpQAZytBoHQQYA3AGOtpw==", - "dependencies": { - "@opentelemetry/core": "1.21.0", - "@opentelemetry/otlp-exporter-base": "0.48.0", - "@opentelemetry/otlp-transformer": "0.48.0", - "@opentelemetry/resources": "1.21.0", - "@opentelemetry/sdk-trace-base": "1.21.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.0" - } - }, - "node_modules/@opentelemetry/exporter-trace-otlp-proto": { - "version": "0.48.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-proto/-/exporter-trace-otlp-proto-0.48.0.tgz", - "integrity": "sha512-hVXr/8DYlAKAzQYMsCf3ZsGweS6NTK3IHIEqmLokJZYcvJQBEEazeAdISfrL/utWnapg1Qnpw8u+W6SpxNzmTw==", - "dependencies": { - "@opentelemetry/core": "1.21.0", - "@opentelemetry/otlp-exporter-base": "0.48.0", - "@opentelemetry/otlp-proto-exporter-base": "0.48.0", - "@opentelemetry/otlp-transformer": "0.48.0", - "@opentelemetry/resources": "1.21.0", - "@opentelemetry/sdk-trace-base": "1.21.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.0" - } - }, - "node_modules/@opentelemetry/exporter-zipkin": { - "version": "1.21.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-zipkin/-/exporter-zipkin-1.21.0.tgz", - "integrity": "sha512-J0ejrOx52s1PqvjNalIHvY/4v9ZxR2r7XS7WZbwK3qpVYZlGVq5V1+iCNweqsKnb/miUt/4TFvJBc9f5Q/kGcA==", - "dependencies": { - "@opentelemetry/core": "1.21.0", - "@opentelemetry/resources": "1.21.0", - "@opentelemetry/sdk-trace-base": "1.21.0", - "@opentelemetry/semantic-conventions": "1.21.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.0" - } - }, - "node_modules/@opentelemetry/id-generator-aws-xray": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/id-generator-aws-xray/-/id-generator-aws-xray-1.2.1.tgz", - "integrity": "sha512-C3CkZuIquW+mb6/nBAW81CPkAVFIOarPorLr7dghiGLN4ksDUaSRHNg2PJhGMRy2SsJknOq/oluW0IvO4Z1BzQ==", - "dependencies": { - "@opentelemetry/core": "^1.0.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation": { - "version": "0.46.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.46.0.tgz", - "integrity": "sha512-a9TijXZZbk0vI5TGLZl+0kxyFfrXHhX6Svtz7Pp2/VBlCSKrazuULEyoJQrOknJyFWNMEmbbJgOciHCCpQcisw==", - "dependencies": { - "@types/shimmer": "^1.0.2", - "import-in-the-middle": "1.7.1", - "require-in-the-middle": "^7.1.1", - "semver": "^7.5.2", - "shimmer": "^1.2.1" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-amqplib": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-amqplib/-/instrumentation-amqplib-0.33.5.tgz", - "integrity": "sha512-WQ/XPzNLOHL3fpsmgoQUkiKCkJ09hvPN8wGrGzzOHMiJ5/3LqvfvxsJ4Rcd6aWkA4il3hEfpl+V0VF0t/DP65A==", - "dependencies": { - "@opentelemetry/core": "^1.8.0", - "@opentelemetry/instrumentation": "^0.46.0", - "@opentelemetry/semantic-conventions": "^1.0.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-aws-lambda": { - "version": "0.37.4", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-aws-lambda/-/instrumentation-aws-lambda-0.37.4.tgz", - "integrity": "sha512-/wdZwUalIWAbxeycvmE+25c1xCMhe5EUuj8bN0MWWN3L8N2SYvfv6DmiRgwrTIPXRgIyFugh2udNiF4MezZN4Q==", - "dependencies": { - "@opentelemetry/instrumentation": "^0.46.0", - "@opentelemetry/propagator-aws-xray": "^1.3.1", - "@opentelemetry/resources": "^1.8.0", - "@opentelemetry/semantic-conventions": "^1.0.0", - "@types/aws-lambda": "8.10.122" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-aws-sdk": { - "version": "0.37.2", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-aws-sdk/-/instrumentation-aws-sdk-0.37.2.tgz", - "integrity": "sha512-eFHHOk0P9EFQ9Ho+2w7KH9R8cH7hut0/ePSsrk0nAM6Tiq2lBPeHn8ialCWESAA9jSjy+iuDelwmqAEXEe+jrg==", - "dependencies": { - "@opentelemetry/core": "^1.8.0", - "@opentelemetry/instrumentation": "^0.46.0", - "@opentelemetry/propagation-utils": "^0.30.5", - "@opentelemetry/semantic-conventions": "^1.0.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-bunyan": { - "version": "0.34.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-bunyan/-/instrumentation-bunyan-0.34.1.tgz", - "integrity": "sha512-+eshbCFr2dkUYO2jCpbYGFC5hs94UCOsQRK1XqNOjeiNvQRtqvKYqk8ARwJBYBX+aW4J02jOliAHQUh/d7gYPg==", - "dependencies": { - "@opentelemetry/api-logs": "^0.46.0", - "@opentelemetry/instrumentation": "^0.46.0", - "@types/bunyan": "1.8.9" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-bunyan/node_modules/@opentelemetry/api-logs": { - "version": "0.46.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.46.0.tgz", - "integrity": "sha512-+9BcqfiEDGPXEIo+o3tso/aqGM5dGbGwAkGVp3FPpZ8GlkK1YlaKRd9gMVyPaeRATwvO5wYGGnCsAc/sMMM9Qw==", - "dependencies": { - "@opentelemetry/api": "^1.0.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/@opentelemetry/instrumentation-cassandra-driver": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-cassandra-driver/-/instrumentation-cassandra-driver-0.34.2.tgz", - "integrity": "sha512-wuzq7QQ9o7PJnzseblNfBcURtM+9AwO6e1m644QYtAb/6YRR6qg6gAmAipVeQu01H5BuHBFC/92svaAkdIV2WQ==", - "dependencies": { - "@opentelemetry/instrumentation": "^0.46.0", - "@opentelemetry/semantic-conventions": "^1.0.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-connect": { - "version": "0.32.4", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-connect/-/instrumentation-connect-0.32.4.tgz", - "integrity": "sha512-oUGph9idncdnHlQMwdIs6m6mii7Kdp9PpHkM9roOZy71h+2vvf6+cVn45bs2unBbE2Vxho2i/049QQbaYKDYlw==", - "dependencies": { - "@opentelemetry/core": "^1.8.0", - "@opentelemetry/instrumentation": "^0.46.0", - "@opentelemetry/semantic-conventions": "^1.0.0", - "@types/connect": "3.4.36" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-cucumber": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-cucumber/-/instrumentation-cucumber-0.2.1.tgz", - "integrity": "sha512-ydF0DpmE0D6wccAbxx1F+6kokzcSSRy3X78Bvgok/3fHUSSshGLErqNiQL1HV44OIcV6392P3tu/jtXtUq3UDQ==", - "dependencies": { - "@opentelemetry/instrumentation": "^0.46.0", - "@opentelemetry/semantic-conventions": "^1.0.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-dataloader": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-dataloader/-/instrumentation-dataloader-0.5.4.tgz", - "integrity": "sha512-l1qQvvygxZJw+S+4hgYgzvT4GArqBrar42wzB5LVsOy04+gmbDw/4y7IqxZYepFyXKuBownGS8pR4huRL/Tj/A==", - "dependencies": { - "@opentelemetry/instrumentation": "^0.46.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-dns": { - "version": "0.32.5", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-dns/-/instrumentation-dns-0.32.5.tgz", - "integrity": "sha512-fHJqrbezpZSipo4O9nF9yGq6R8oyr1W2gSlyk1foJNXBaqdCODTlzIa7BP50vGtLBN/m+qO8pMOWCJmYSBX35g==", - "dependencies": { - "@opentelemetry/instrumentation": "^0.46.0", - "@opentelemetry/semantic-conventions": "^1.0.0", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-express": { - "version": "0.34.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-express/-/instrumentation-express-0.34.1.tgz", - "integrity": "sha512-pQBQxXpkH6imvzwCdPcw2FKjB1cphoRpmWTiGi6vtBdKXCP0hpne613ycpwhGG7C17S+mbarVmukbJKR4rmh6Q==", - "dependencies": { - "@opentelemetry/core": "^1.8.0", - "@opentelemetry/instrumentation": "^0.46.0", - "@opentelemetry/semantic-conventions": "^1.0.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-fastify": { - "version": "0.32.6", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-fastify/-/instrumentation-fastify-0.32.6.tgz", - "integrity": "sha512-UkBu8rAqeVC034jsRMiEAmYhFQ03pvmE/MnoPKE9gAbgVtPILdekHYqAKM0MdqnEjW7pO45t4wWsbtIcN0eiBw==", - "dependencies": { - "@opentelemetry/core": "^1.8.0", - "@opentelemetry/instrumentation": "^0.46.0", - "@opentelemetry/semantic-conventions": "^1.0.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-fs": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-fs/-/instrumentation-fs-0.8.4.tgz", - "integrity": "sha512-g99963nK8TuisUM3KH+ee5hOCHdCHSKiAdmy0RMdiKT7ITh3rXUct7fghQibViQA7FVPkdwM9+uRKkigJSFS9w==", - "dependencies": { - "@opentelemetry/core": "^1.8.0", - "@opentelemetry/instrumentation": "^0.46.0", - "@opentelemetry/semantic-conventions": "^1.0.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-generic-pool": { - "version": "0.32.5", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-generic-pool/-/instrumentation-generic-pool-0.32.5.tgz", - "integrity": "sha512-MNFloXa3SDg/rHh397JnWADr7iYQ6HHRwT7ZId5Vt0L1Xx+Qp3XSIILsXk5qTXVUIytEIVgn8iMT+kZILHzSqg==", - "dependencies": { - "@opentelemetry/instrumentation": "^0.46.0", - "@opentelemetry/semantic-conventions": "^1.0.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-graphql": { - "version": "0.36.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-graphql/-/instrumentation-graphql-0.36.1.tgz", - "integrity": "sha512-EOvaS+d2909TOJJjNTsb0vHaLg8WdTLoQS8bRKdy3lMgdd7I4OL9/LSC7dp4M8CvJKz9B464Ix9PnARvhMkNOw==", - "dependencies": { - "@opentelemetry/instrumentation": "^0.46.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-grpc": { - "version": "0.46.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-grpc/-/instrumentation-grpc-0.46.0.tgz", - "integrity": "sha512-KemIpB4jmywQv/+MbVoUIMVp3vr+rzra37TYbN7kTsbrn213YlzdXVamf6nq/yChI6q+9JlUnCdSZf86D6NO6g==", - "dependencies": { - "@opentelemetry/instrumentation": "0.46.0", - "@opentelemetry/semantic-conventions": "1.19.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-grpc/node_modules/@opentelemetry/semantic-conventions": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.19.0.tgz", - "integrity": "sha512-14jRpC8f5c0gPSwoZ7SbEJni1PqI+AhAE8m1bMz6v+RPM4OlP1PT2UHBJj5Qh/ALLPjhVU/aZUK3YyjTUqqQVg==", - "engines": { - "node": ">=14" - } - }, - "node_modules/@opentelemetry/instrumentation-hapi": { - "version": "0.33.3", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-hapi/-/instrumentation-hapi-0.33.3.tgz", - "integrity": "sha512-l14u1TFPXMUjfHqnrHtzuMyLq6V8BikwhYJO/hIgkbaPCxS38TloCtDLJvcs8S8AZlcQfkUqE/NFgXYETZRo+Q==", - "dependencies": { - "@opentelemetry/core": "^1.8.0", - "@opentelemetry/instrumentation": "^0.46.0", - "@opentelemetry/semantic-conventions": "^1.0.0", - "@types/hapi__hapi": "20.0.13" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-http": { - "version": "0.46.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-http/-/instrumentation-http-0.46.0.tgz", - "integrity": "sha512-t5cxgqfV9AcxVP00/OL1ggkOSZM57VXDpvlWaOidYyyfLKcUJ9e2fGbNwoVsGFboRDeH0iFo7gLA3EEvX13wCA==", - "dependencies": { - "@opentelemetry/core": "1.19.0", - "@opentelemetry/instrumentation": "0.46.0", - "@opentelemetry/semantic-conventions": "1.19.0", - "semver": "^7.5.2" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-http/node_modules/@opentelemetry/core": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.19.0.tgz", - "integrity": "sha512-w42AukJh3TP8R0IZZOVJVM/kMWu8g+lm4LzT70WtuKqhwq7KVhcDzZZuZinWZa6TtQCl7Smt2wolEYzpHabOgw==", - "dependencies": { - "@opentelemetry/semantic-conventions": "1.19.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.8.0" - } - }, - "node_modules/@opentelemetry/instrumentation-http/node_modules/@opentelemetry/semantic-conventions": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.19.0.tgz", - "integrity": "sha512-14jRpC8f5c0gPSwoZ7SbEJni1PqI+AhAE8m1bMz6v+RPM4OlP1PT2UHBJj5Qh/ALLPjhVU/aZUK3YyjTUqqQVg==", - "engines": { - "node": ">=14" - } - }, - "node_modules/@opentelemetry/instrumentation-ioredis": { - "version": "0.36.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-ioredis/-/instrumentation-ioredis-0.36.1.tgz", - "integrity": "sha512-xxab7n4TD5igCFR5N0j5PJFYVeOXm54ZtCARVS32xavwGyGf+Sb6VtuVCLdl0re4JENCg18FO97Dyb1ql2EBUA==", - "dependencies": { - "@opentelemetry/instrumentation": "^0.46.0", - "@opentelemetry/redis-common": "^0.36.1", - "@opentelemetry/semantic-conventions": "^1.0.0", - "@types/ioredis4": "npm:@types/ioredis@^4.28.10" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-knex": { - "version": "0.32.4", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-knex/-/instrumentation-knex-0.32.4.tgz", - "integrity": "sha512-vqxK1wqhktQnBA7nGr6nqfhV5irSXK9v0R29hqlyTr/cB/86Hn3Z98zH58QvHo131FcE+d70QdiXu3P9S5vq4g==", - "dependencies": { - "@opentelemetry/instrumentation": "^0.46.0", - "@opentelemetry/semantic-conventions": "^1.0.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-koa": { - "version": "0.36.4", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-koa/-/instrumentation-koa-0.36.4.tgz", - "integrity": "sha512-Qt6IF0mo3FZZ+x4NQhv/pViDtPSw/h/QYDvyIqMCuqUibqta619WLUCwAcanKnZWvzMuYh6lT0TnZ8ktjXo6jQ==", - "dependencies": { - "@opentelemetry/core": "^1.8.0", - "@opentelemetry/instrumentation": "^0.46.0", - "@opentelemetry/semantic-conventions": "^1.0.0", - "@types/koa": "2.13.9", - "@types/koa__router": "12.0.3" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-lru-memoizer": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-lru-memoizer/-/instrumentation-lru-memoizer-0.33.5.tgz", - "integrity": "sha512-vbm9QPEYD3FZNGSa+7xQ7uOkgbJtskIwp1KnsCpmdBBiulhBaqqgeNLFo7gd8UxMrI2Vu3LTlZil2D0dVt8g/A==", - "dependencies": { - "@opentelemetry/instrumentation": "^0.46.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-memcached": { - "version": "0.32.5", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-memcached/-/instrumentation-memcached-0.32.5.tgz", - "integrity": "sha512-1MS9LfgZruAsWOHVDTI+/Wy9mFFivUlpGUwYC4D+ho1I4NUyE33XHwL1BKcjMupkuRnE0JmbhdBfTG9Ai1vFkw==", - "dependencies": { - "@opentelemetry/instrumentation": "^0.46.0", - "@opentelemetry/semantic-conventions": "^1.0.0", - "@types/memcached": "^2.2.6" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-mongodb": { - "version": "0.38.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongodb/-/instrumentation-mongodb-0.38.1.tgz", - "integrity": "sha512-X6YjE8dOCf8lG8FGmoAvczZq7LtgYaRzZcLGthZSUJQ2rfp1JJRlJixc+COvhrn1HJj5ab+AsSdUQgTpfQgEHQ==", - "dependencies": { - "@opentelemetry/instrumentation": "^0.46.0", - "@opentelemetry/sdk-metrics": "^1.9.1", - "@opentelemetry/semantic-conventions": "^1.0.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-mongoose": { - "version": "0.34.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongoose/-/instrumentation-mongoose-0.34.0.tgz", - "integrity": "sha512-/wGNK7qR/QXpcidXntKsnfACHETp8G/f2o/k8FPvJ2lukvdM9r7cHLys8QwkJkTN8Kt3qG1VIwarGKYtE/zOSw==", - "dependencies": { - "@opentelemetry/core": "^1.8.0", - "@opentelemetry/instrumentation": "^0.46.0", - "@opentelemetry/semantic-conventions": "^1.0.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-mysql": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql/-/instrumentation-mysql-0.34.5.tgz", - "integrity": "sha512-cE8z1uJTeLcMj+R31t1pLkLqt3ryGMl1HApxsqqf8YCSHetrkVwGZOcyQ3phfgGSaNlC4/pdf3CQqfjhXbLWlA==", - "dependencies": { - "@opentelemetry/instrumentation": "^0.46.0", - "@opentelemetry/semantic-conventions": "^1.0.0", - "@types/mysql": "2.15.22" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-mysql2": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql2/-/instrumentation-mysql2-0.34.5.tgz", - "integrity": "sha512-Ai3+cJ83b11kLypIVEPLHuYCiIQjf828hHJPpllr78EhmHiq4S1yTW34aP3BzCBY+5Adr5PS5Nnv7NLBI/YfaQ==", - "dependencies": { - "@opentelemetry/instrumentation": "^0.46.0", - "@opentelemetry/semantic-conventions": "^1.0.0", - "@opentelemetry/sql-common": "^0.40.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-nestjs-core": { - "version": "0.33.4", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-nestjs-core/-/instrumentation-nestjs-core-0.33.4.tgz", - "integrity": "sha512-AynD6TesE0bZg9UzS4ZLG//6TmU8GkRrjubhxs7jYZ3JRAlJAq1In0zSwM082JOIs7wr286nhs1FmqK91cG1cg==", - "dependencies": { - "@opentelemetry/instrumentation": "^0.46.0", - "@opentelemetry/semantic-conventions": "^1.0.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-net": { - "version": "0.32.5", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-net/-/instrumentation-net-0.32.5.tgz", - "integrity": "sha512-omBLTXyJeCtBy1MzVi+IzUcpXiup90k0z3AGhNKbzro4RhpsdMpN9O+3zZCXCIHMuyifhy7z8w99wmvvFO/FCQ==", - "dependencies": { - "@opentelemetry/instrumentation": "^0.46.0", - "@opentelemetry/semantic-conventions": "^1.0.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-pg": { - "version": "0.37.2", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pg/-/instrumentation-pg-0.37.2.tgz", - "integrity": "sha512-MAiKqdtGItYjvD6rOCyGS27CdMaDnh2JuImIHXhrPjq/sb2JlBNm6m1e4BH4uik1VfcKt/I3pI3UkydSWIscCg==", - "dependencies": { - "@opentelemetry/core": "^1.8.0", - "@opentelemetry/instrumentation": "^0.46.0", - "@opentelemetry/semantic-conventions": "^1.0.0", - "@opentelemetry/sql-common": "^0.40.0", - "@types/pg": "8.6.1", - "@types/pg-pool": "2.0.4" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-pino": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pino/-/instrumentation-pino-0.34.5.tgz", - "integrity": "sha512-auYDSeFhkPFXayT/060mQRL7O88Bt5NKcV0qOfquNa9J5/qs5dlJYdTOraxPrjiGPM3tHaAJ+AvAhR0CPYgZew==", - "dependencies": { - "@opentelemetry/instrumentation": "^0.46.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-redis": { - "version": "0.35.5", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-redis/-/instrumentation-redis-0.35.5.tgz", - "integrity": "sha512-UPYUncDlLqDPtyU11UhyZOUxAyPQS6yQGT0b96KjpqMhmuRb3b0WxzZh3SoIaAyprL5f9fxyeV2HfSulR0aWFQ==", - "dependencies": { - "@opentelemetry/instrumentation": "^0.46.0", - "@opentelemetry/redis-common": "^0.36.1", - "@opentelemetry/semantic-conventions": "^1.0.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-redis-4": { - "version": "0.35.6", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-redis-4/-/instrumentation-redis-4-0.35.6.tgz", - "integrity": "sha512-OVSUJZAuy6OX18X2TKPdPlpwM5t4FooJU9QXiUxezhdMvfIAu00Agchw+gRbszkM7nvQ9dkXFOZO3nTmJNcLcA==", - "dependencies": { - "@opentelemetry/instrumentation": "^0.46.0", - "@opentelemetry/redis-common": "^0.36.1", - "@opentelemetry/semantic-conventions": "^1.0.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-restify": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-restify/-/instrumentation-restify-0.34.3.tgz", - "integrity": "sha512-nUqf4RTcQyc/YTWMT0e/ZqvuQqhRH04MOoSwaH6ocmyrEhKdPDq9AbvSMerQ/AxNC9yju/PytgzFFWH45hh3KA==", - "dependencies": { - "@opentelemetry/core": "^1.8.0", - "@opentelemetry/instrumentation": "^0.46.0", - "@opentelemetry/semantic-conventions": "^1.0.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-router": { - "version": "0.33.4", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-router/-/instrumentation-router-0.33.4.tgz", - "integrity": "sha512-4140BgILsL0H8tA0BBN2tjzajgjxlDqd4xPatk2i5q9ofy8wlB0BlDJ4m36U7G1z0v+a+LshQSNqPP2VHZ9ORw==", - "dependencies": { - "@opentelemetry/instrumentation": "^0.46.0", - "@opentelemetry/semantic-conventions": "^1.0.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-socket.io": { - "version": "0.35.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-socket.io/-/instrumentation-socket.io-0.35.0.tgz", - "integrity": "sha512-ED1/Wco05H9fepKqX7n2kONq9EXxkBZTKS29nUH9vVL7wSIT8sBIDqpHz94CnQ8xuicaQQ7c5h9TVuhjtzV43Q==", - "dependencies": { - "@opentelemetry/instrumentation": "^0.46.0", - "@opentelemetry/semantic-conventions": "^1.0.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-tedious": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-tedious/-/instrumentation-tedious-0.6.5.tgz", - "integrity": "sha512-jjdrDegLUodz9np0yKCAa7sLxlAGTsxM7wU7l1mQ2NM6PHAGv4X3eSFClUk3fOipLx4+r5jTLnlsgu7g9CW+Qw==", - "dependencies": { - "@opentelemetry/instrumentation": "^0.46.0", - "@opentelemetry/semantic-conventions": "^1.0.0", - "@types/tedious": "^4.0.10" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-winston": { - "version": "0.33.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-winston/-/instrumentation-winston-0.33.1.tgz", - "integrity": "sha512-4tSORoAY9f+yzqNVGcGr/3GydPMfgSiKK1OESc+qBwVTz0bmz4cOrhCruCngGzoqDCmPYpwqwR/8j4wRKgcUpw==", - "dependencies": { - "@opentelemetry/instrumentation": "^0.46.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/otlp-exporter-base": { - "version": "0.48.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.48.0.tgz", - "integrity": "sha512-T4LJND+Ugl87GUONoyoQzuV9qCn4BFIPOnCH1biYqdGhc2JahjuLqVD9aefwLzGBW638iLAo88Lh68h2F1FLiA==", - "dependencies": { - "@opentelemetry/core": "1.21.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.0" - } - }, - "node_modules/@opentelemetry/otlp-grpc-exporter-base": { - "version": "0.48.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-grpc-exporter-base/-/otlp-grpc-exporter-base-0.48.0.tgz", - "integrity": "sha512-Vdp56RK9OU+Oeoy3YQC/UMOWglKQ9qvgGr49FgF4r8vk5DlcTUgVS0m3KG8pykmRPA+5ZKaDuqwPw5aTvWmHFw==", - "dependencies": { - "@grpc/grpc-js": "^1.7.1", - "@opentelemetry/core": "1.21.0", - "@opentelemetry/otlp-exporter-base": "0.48.0", - "protobufjs": "^7.2.3" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.0" - } - }, - "node_modules/@opentelemetry/otlp-proto-exporter-base": { - "version": "0.48.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-proto-exporter-base/-/otlp-proto-exporter-base-0.48.0.tgz", - "integrity": "sha512-14GSTvPZPfrWsB54fYMGb8v+Uge5xGXyz0r2rf4SzcRnO2hXCPHEuL3yyL50emaKPAY+fj29Dm0bweawe8UA6A==", - "dependencies": { - "@opentelemetry/core": "1.21.0", - "@opentelemetry/otlp-exporter-base": "0.48.0", - "protobufjs": "^7.2.3" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.0" - } - }, - "node_modules/@opentelemetry/otlp-transformer": { - "version": "0.48.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.48.0.tgz", - "integrity": "sha512-yuoS4cUumaTK/hhxW3JUy3wl2U4keMo01cFDrUOmjloAdSSXvv1zyQ920IIH4lymp5Xd21Dj2/jq2LOro56TJg==", - "dependencies": { - "@opentelemetry/api-logs": "0.48.0", - "@opentelemetry/core": "1.21.0", - "@opentelemetry/resources": "1.21.0", - "@opentelemetry/sdk-logs": "0.48.0", - "@opentelemetry/sdk-metrics": "1.21.0", - "@opentelemetry/sdk-trace-base": "1.21.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.3.0 <1.8.0" - } - }, - "node_modules/@opentelemetry/propagation-utils": { - "version": "0.30.6", - "resolved": "https://registry.npmjs.org/@opentelemetry/propagation-utils/-/propagation-utils-0.30.6.tgz", - "integrity": "sha512-qvnYee5I/xrBY5XClUlOASIOdoTbnFGNI6+ViKqdoErF2xKmhysXcmxlJNzQFNDK0muTfjvJMZcFyEielARk7g==", - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.0" - } - }, - "node_modules/@opentelemetry/propagator-aws-xray": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-aws-xray/-/propagator-aws-xray-1.3.1.tgz", - "integrity": "sha512-6fDMzFlt5r6VWv7MUd0eOpglXPFqykW8CnOuUxJ1VZyLy6mV1bzBlzpsqEmhx1bjvZYvH93vhGkQZqrm95mlrQ==", - "dependencies": { - "@opentelemetry/core": "^1.0.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.0" - } - }, - "node_modules/@opentelemetry/propagator-b3": { - "version": "1.21.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-1.21.0.tgz", - "integrity": "sha512-3ZTobj2VDIOzLsIvvYCdpw6tunxUVElPxDvog9lS49YX4hohHeD84A8u9Ns/6UYUcaN5GSoEf891lzhcBFiOLA==", - "dependencies": { - "@opentelemetry/core": "1.21.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.8.0" - } - }, - "node_modules/@opentelemetry/propagator-jaeger": { - "version": "1.21.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-1.21.0.tgz", - "integrity": "sha512-8TQSwXjBmaDx7JkxRD7hdmBmRK2RGRgzHX1ArJfJhIc5trzlVweyorzqQrXOvqVEdEg+zxUMHkL5qbGH/HDTPA==", - "dependencies": { - "@opentelemetry/core": "1.21.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.8.0" - } - }, - "node_modules/@opentelemetry/redis-common": { - "version": "0.36.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/redis-common/-/redis-common-0.36.1.tgz", - "integrity": "sha512-YjfNEr7DK1Ymc5H0bzhmqVvMcCs+PUEUerzrpTFdHfZxj3HpnnjZTIFKx/gxiL/sajQ8dxycjlreoYTVYKBXlw==", - "engines": { - "node": ">=14" - } - }, - "node_modules/@opentelemetry/resource-detector-alibaba-cloud": { - "version": "0.28.6", - "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-alibaba-cloud/-/resource-detector-alibaba-cloud-0.28.6.tgz", - "integrity": "sha512-VuJXc+oDQ/SYRHBCQbEshl0WJtEMvgfSkTDBq+WSxj6y9sKe0HCt55Sxeb5nLmBdtCYWMQFniHe2K4GLSjDZVw==", - "dependencies": { - "@opentelemetry/resources": "^1.0.0", - "@opentelemetry/semantic-conventions": "^1.0.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.0" - } - }, - "node_modules/@opentelemetry/resource-detector-aws": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-aws/-/resource-detector-aws-1.3.6.tgz", - "integrity": "sha512-hFJ19yFwChqGCv1uMkXtjZU9BG8GcChe8cRCAkGWg1RZADse5S2ausf3D8pYw1cR3ktJtuAmRrGZniT6TDUPDw==", - "dependencies": { - "@opentelemetry/core": "^1.0.0", - "@opentelemetry/resources": "^1.0.0", - "@opentelemetry/semantic-conventions": "^1.0.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.0" - } - }, - "node_modules/@opentelemetry/resource-detector-container": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-container/-/resource-detector-container-0.3.6.tgz", - "integrity": "sha512-psxtzQakVuZKFl/ukn+nPW4Ixn+KPHGsWJMYKndmXrsgdFri78X+MHR0wLOO41Zn79sc35DiSk+GdJ24cCylbg==", - "dependencies": { - "@opentelemetry/resources": "^1.0.0", - "@opentelemetry/semantic-conventions": "^1.0.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.0" - } - }, - "node_modules/@opentelemetry/resource-detector-gcp": { - "version": "0.29.6", - "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-gcp/-/resource-detector-gcp-0.29.6.tgz", - "integrity": "sha512-cx03fXPknmiVW0hpWAJr0Nr8xwkwRB8VNWPvNrmP7UzJ8eEztY9lHnVke4ZVFaVGvm/4EFxk2y5hPNggbIezoA==", - "dependencies": { - "@opentelemetry/core": "^1.0.0", - "@opentelemetry/resources": "^1.0.0", - "@opentelemetry/semantic-conventions": "^1.0.0", - "gcp-metadata": "^6.0.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.0" - } - }, - "node_modules/@opentelemetry/resources": { - "version": "1.21.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.21.0.tgz", - "integrity": "sha512-1Z86FUxPKL6zWVy2LdhueEGl9AHDJcx+bvHStxomruz6Whd02mE3lNUMjVJ+FGRoktx/xYQcxccYb03DiUP6Yw==", - "dependencies": { - "@opentelemetry/core": "1.21.0", - "@opentelemetry/semantic-conventions": "1.21.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.8.0" - } - }, - "node_modules/@opentelemetry/sdk-logs": { - "version": "0.48.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.48.0.tgz", - "integrity": "sha512-lRcA5/qkSJuSh4ItWCddhdn/nNbVvnzM+cm9Fg1xpZUeTeozjJDBcHnmeKoOaWRnrGYBdz6UTY6bynZR9aBeAA==", - "dependencies": { - "@opentelemetry/core": "1.21.0", - "@opentelemetry/resources": "1.21.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.4.0 <1.8.0", - "@opentelemetry/api-logs": ">=0.39.1" - } - }, - "node_modules/@opentelemetry/sdk-metrics": { - "version": "1.21.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.21.0.tgz", - "integrity": "sha512-on1jTzIHc5DyWhRP+xpf+zrgrREXcHBH4EDAfaB5mIG7TWpKxNXooQ1JCylaPsswZUv4wGnVTinr4HrBdGARAQ==", - "dependencies": { - "@opentelemetry/core": "1.21.0", - "@opentelemetry/resources": "1.21.0", - "lodash.merge": "^4.6.2" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.3.0 <1.8.0" - } - }, - "node_modules/@opentelemetry/sdk-node": { - "version": "0.48.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-node/-/sdk-node-0.48.0.tgz", - "integrity": "sha512-3o3GS6t+VLGVFCV5bqfGOcWIgOdkR/UE6Qz7hHksP5PXrVBeYsPqts7cPma5YXweaI3r3h26mydg9PqQIcqksg==", - "dependencies": { - "@opentelemetry/api-logs": "0.48.0", - "@opentelemetry/core": "1.21.0", - "@opentelemetry/exporter-trace-otlp-grpc": "0.48.0", - "@opentelemetry/exporter-trace-otlp-http": "0.48.0", - "@opentelemetry/exporter-trace-otlp-proto": "0.48.0", - "@opentelemetry/exporter-zipkin": "1.21.0", - "@opentelemetry/instrumentation": "0.48.0", - "@opentelemetry/resources": "1.21.0", - "@opentelemetry/sdk-logs": "0.48.0", - "@opentelemetry/sdk-metrics": "1.21.0", - "@opentelemetry/sdk-trace-base": "1.21.0", - "@opentelemetry/sdk-trace-node": "1.21.0", - "@opentelemetry/semantic-conventions": "1.21.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.3.0 <1.8.0" - } - }, - "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/instrumentation": { - "version": "0.48.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.48.0.tgz", - "integrity": "sha512-sjtZQB5PStIdCw5ovVTDGwnmQC+GGYArJNgIcydrDSqUTdYBnMrN9P4pwQZgS3vTGIp+TU1L8vMXGe51NVmIKQ==", - "dependencies": { - "@types/shimmer": "^1.0.2", - "import-in-the-middle": "1.7.1", - "require-in-the-middle": "^7.1.1", - "semver": "^7.5.2", - "shimmer": "^1.2.1" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/sdk-trace-base": { - "version": "1.21.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.21.0.tgz", - "integrity": "sha512-yrElGX5Fv0umzp8Nxpta/XqU71+jCAyaLk34GmBzNcrW43nqbrqvdPs4gj4MVy/HcTjr6hifCDCYA3rMkajxxA==", - "dependencies": { - "@opentelemetry/core": "1.21.0", - "@opentelemetry/resources": "1.21.0", - "@opentelemetry/semantic-conventions": "1.21.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.8.0" - } - }, - "node_modules/@opentelemetry/sdk-trace-node": { - "version": "1.21.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-1.21.0.tgz", - "integrity": "sha512-1pdm8jnqs+LuJ0Bvx6sNL28EhC8Rv7NYV8rnoXq3GIQo7uOHBDAFSj7makAfbakrla7ecO1FRfI8emnR4WvhYA==", - "dependencies": { - "@opentelemetry/context-async-hooks": "1.21.0", - "@opentelemetry/core": "1.21.0", - "@opentelemetry/propagator-b3": "1.21.0", - "@opentelemetry/propagator-jaeger": "1.21.0", - "@opentelemetry/sdk-trace-base": "1.21.0", - "semver": "^7.5.2" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.8.0" - } - }, - "node_modules/@opentelemetry/semantic-conventions": { - "version": "1.21.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.21.0.tgz", - "integrity": "sha512-lkC8kZYntxVKr7b8xmjCVUgE0a8xgDakPyDo9uSWavXPyYqLgYYGdEd2j8NxihRyb6UwpX3G/hFUF4/9q2V+/g==", - "engines": { - "node": ">=14" - } - }, - "node_modules/@opentelemetry/sql-common": { - "version": "0.40.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sql-common/-/sql-common-0.40.0.tgz", - "integrity": "sha512-vSqRJYUPJVjMFQpYkQS3ruexCPSZJ8esne3LazLwtCPaPRvzZ7WG3tX44RouAn7w4wMp8orKguBqtt+ng2UTnw==", - "dependencies": { - "@opentelemetry/core": "^1.1.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.1.0" - } - }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "optional": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@pkgr/core": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", - "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", - "dev": true, - "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/unts" - } - }, - "node_modules/@protobufjs/aspromise": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" - }, - "node_modules/@protobufjs/base64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" - }, - "node_modules/@protobufjs/codegen": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", - "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" - }, - "node_modules/@protobufjs/eventemitter": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", - "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" - }, - "node_modules/@protobufjs/fetch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", - "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", - "dependencies": { - "@protobufjs/aspromise": "^1.1.1", - "@protobufjs/inquire": "^1.1.0" - } - }, - "node_modules/@protobufjs/float": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", - "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" - }, - "node_modules/@protobufjs/inquire": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", - "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" - }, - "node_modules/@protobufjs/path": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", - "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" - }, - "node_modules/@protobufjs/pool": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", - "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" - }, - "node_modules/@protobufjs/utf8": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", - "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" - }, - "node_modules/@purista/amqpbridge": { - "resolved": "packages/amqpbridge", - "link": true - }, - "node_modules/@purista/aws-config-store": { - "resolved": "packages/aws-config-store", - "link": true - }, - "node_modules/@purista/aws-secret-store": { - "resolved": "packages/aws-secret-store", - "link": true - }, - "node_modules/@purista/azure-secret-store": { - "resolved": "packages/azure-secret-store", - "link": true - }, - "node_modules/@purista/base-http-bridge": { - "resolved": "packages/base-http-bridge", - "link": true - }, - "node_modules/@purista/cli": { - "resolved": "packages/cli", - "link": true - }, - "node_modules/@purista/core": { - "resolved": "packages/core", - "link": true - }, - "node_modules/@purista/dapr-example": { - "resolved": "examples/dapr-example", - "link": true - }, - "node_modules/@purista/dapr-sdk": { - "resolved": "packages/dapr-sdk", - "link": true - }, - "node_modules/@purista/full-example": { - "resolved": "examples/fullexample", - "link": true - }, - "node_modules/@purista/gcloud-secret-store": { - "resolved": "packages/gcloud-secret-store", - "link": true - }, - "node_modules/@purista/hono-example": { - "resolved": "examples/hono-example", - "link": true - }, - "node_modules/@purista/hono-http-server": { - "resolved": "packages/hono-http-server", - "link": true - }, - "node_modules/@purista/httpserver": { - "resolved": "packages/httpserver", - "link": true - }, - "node_modules/@purista/infisical-secret-store": { - "resolved": "packages/infisical-secret-store", - "link": true - }, - "node_modules/@purista/k8s-sdk": { - "resolved": "packages/k8s-sdk", - "link": true - }, - "node_modules/@purista/kubernetes-example": { - "resolved": "examples/kubernetes", - "link": true - }, - "node_modules/@purista/mqtt-example": { - "resolved": "examples/mqtt-bridge", - "link": true - }, - "node_modules/@purista/mqttbridge": { - "resolved": "packages/mqttbridge", - "link": true - }, - "node_modules/@purista/nats-config-store": { - "resolved": "packages/nats-config-store", - "link": true - }, - "node_modules/@purista/nats-example": { - "resolved": "examples/nats-bridge", - "link": true - }, - "node_modules/@purista/nats-state-store": { - "resolved": "packages/nats-state-store", - "link": true - }, - "node_modules/@purista/natsbridge": { - "resolved": "packages/natsbridge", - "link": true - }, - "node_modules/@purista/quickstart": { - "resolved": "examples/quickstart", - "link": true - }, - "node_modules/@purista/redis-config-store": { - "resolved": "packages/redis-config-store", - "link": true - }, - "node_modules/@purista/redis-state-store": { - "resolved": "packages/redis-state-store", - "link": true - }, - "node_modules/@purista/temporal-example": { - "resolved": "examples/temporal", - "link": true - }, - "node_modules/@purista/website": { - "resolved": "website", - "link": true - }, - "node_modules/@redis/client": { - "version": "1.5.14", - "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.5.14.tgz", - "integrity": "sha512-YGn0GqsRBFUQxklhY7v562VMOP0DcmlrHHs3IV1mFE3cbxe31IITUkqhBcIhVSI/2JqtWAJXg5mjV4aU+zD0HA==", - "dependencies": { - "cluster-key-slot": "1.1.2", - "generic-pool": "3.9.0", - "yallist": "4.0.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.12.0.tgz", - "integrity": "sha512-+ac02NL/2TCKRrJu2wffk1kZ+RyqxVUlbjSagNgPm94frxtr+XDL12E5Ll1enWskLrtrZ2r8L3wED1orIibV/w==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.12.0.tgz", - "integrity": "sha512-OBqcX2BMe6nvjQ0Nyp7cC90cnumt8PXmO7Dp3gfAju/6YwG0Tj74z1vKrfRz7qAv23nBcYM8BCbhrsWqO7PzQQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.12.0.tgz", - "integrity": "sha512-X64tZd8dRE/QTrBIEs63kaOBG0b5GVEd3ccoLtyf6IdXtHdh8h+I56C2yC3PtC9Ucnv0CpNFJLqKFVgCYe0lOQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.12.0.tgz", - "integrity": "sha512-cc71KUZoVbUJmGP2cOuiZ9HSOP14AzBAThn3OU+9LcA1+IUqswJyR1cAJj3Mg55HbjZP6OLAIscbQsQLrpgTOg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.12.0.tgz", - "integrity": "sha512-a6w/Y3hyyO6GlpKL2xJ4IOh/7d+APaqLYdMf86xnczU3nurFTaVN9s9jOXQg97BE4nYm/7Ga51rjec5nfRdrvA==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.12.0.tgz", - "integrity": "sha512-0fZBq27b+D7Ar5CQMofVN8sggOVhEtzFUwOwPppQt0k+VR+7UHMZZY4y+64WJ06XOhBTKXtQB/Sv0NwQMXyNAA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.12.0.tgz", - "integrity": "sha512-eTvzUS3hhhlgeAv6bfigekzWZjaEX9xP9HhxB0Dvrdbkk5w/b+1Sxct2ZuDxNJKzsRStSq1EaEkVSEe7A7ipgQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.12.0.tgz", - "integrity": "sha512-ix+qAB9qmrCRiaO71VFfY8rkiAZJL8zQRXveS27HS+pKdjwUfEhqo2+YF2oI+H/22Xsiski+qqwIBxVewLK7sw==", - "cpu": [ - "riscv64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.12.0.tgz", - "integrity": "sha512-TenQhZVOtw/3qKOPa7d+QgkeM6xY0LtwzR8OplmyL5LrgTWIXpTQg2Q2ycBf8jm+SFW2Wt/DTn1gf7nFp3ssVA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.12.0.tgz", - "integrity": "sha512-LfFdRhNnW0zdMvdCb5FNuWlls2WbbSridJvxOvYWgSBOYZtgBfW9UGNJG//rwMqTX1xQE9BAodvMH9tAusKDUw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.12.0.tgz", - "integrity": "sha512-JPDxovheWNp6d7AHCgsUlkuCKvtu3RB55iNEkaQcf0ttsDU/JZF+iQnYcQJSk/7PtT4mjjVG8N1kpwnI9SLYaw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.12.0.tgz", - "integrity": "sha512-fjtuvMWRGJn1oZacG8IPnzIV6GF2/XG+h71FKn76OYFqySXInJtseAqdprVTDTyqPxQOG9Exak5/E9Z3+EJ8ZA==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.12.0.tgz", - "integrity": "sha512-ZYmr5mS2wd4Dew/JjT0Fqi2NPB/ZhZ2VvPp7SmvPZb4Y1CG/LRcS6tcRo2cYU7zLK5A7cdbhWnnWmUjoI4qapg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@shikijs/core": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.1.7.tgz", - "integrity": "sha512-gTYLUIuD1UbZp/11qozD3fWpUTuMqPSf3svDMMrL0UmlGU7D9dPw/V1FonwAorCUJBltaaESxq90jrSjQyGixg==", - "dev": true - }, - "node_modules/@shikijs/transformers": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@shikijs/transformers/-/transformers-1.1.7.tgz", - "integrity": "sha512-lXz011ao4+rvweps/9h3CchBfzb1U5OtP5D51Tqc9lQYdLblWMIxQxH6Ybe1GeGINcEVM4goMyPrI0JvlIp4UQ==", - "dev": true, - "dependencies": { - "shiki": "1.1.7" - } - }, - "node_modules/@shikijs/transformers/node_modules/shiki": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.1.7.tgz", - "integrity": "sha512-9kUTMjZtcPH3i7vHunA6EraTPpPOITYTdA5uMrvsJRexktqP0s7P3s9HVK80b4pP42FRVe03D7fT3NmJv2yYhw==", - "dev": true, - "dependencies": { - "@shikijs/core": "1.1.7" - } - }, - "node_modules/@sideway/address": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", - "integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==", - "dependencies": { - "@hapi/hoek": "^9.0.0" - } - }, - "node_modules/@sideway/formula": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", - "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==" - }, - "node_modules/@sideway/pinpoint": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", - "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==" - }, - "node_modules/@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "dev": true - }, - "node_modules/@sindresorhus/is": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", - "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/is?sponsor=1" - } - }, - "node_modules/@sinonjs/commons": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", - "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", - "dev": true, - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "11.2.2", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-11.2.2.tgz", - "integrity": "sha512-G2piCSxQ7oWOxwGSAyFHfPIsyeJGXYtc6mFbnFA+kRXkiEnTl8c/8jul2S329iFBnDI9HGoeWWAZvuvOkZccgw==", - "dev": true, - "dependencies": { - "@sinonjs/commons": "^3.0.0" - } - }, - "node_modules/@sinonjs/samsam": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.0.tgz", - "integrity": "sha512-Bp8KUVlLp8ibJZrnvq2foVhP0IVX2CIprMJPK0vqGqgrDa0OHVKeZyBykqskkrdxV6yKBPmGasO8LVjAKR3Gew==", - "dev": true, - "dependencies": { - "@sinonjs/commons": "^2.0.0", - "lodash.get": "^4.4.2", - "type-detect": "^4.0.8" - } - }, - "node_modules/@sinonjs/samsam/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/text-encoding": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz", - "integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==", - "dev": true - }, - "node_modules/@smithy/abort-controller": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-2.1.2.tgz", - "integrity": "sha512-iwUxrFm/ZFCXhzhtZ6JnoJzAsqUrVfBAZUTQj8ypXGtIjwXZpKqmgYiuqrDERiydDI5gesqvsC4Rqe57GGhbVg==", - "dependencies": { - "@smithy/types": "^2.10.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/config-resolver": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-2.1.2.tgz", - "integrity": "sha512-ZDMY63xJVsJl7ei/yIMv9nx8OiEOulwNnQOUDGpIvzoBrcbvYwiMjIMe5mP5J4fUmttKkpiTKwta/7IUriAn9w==", - "dependencies": { - "@smithy/node-config-provider": "^2.2.2", - "@smithy/types": "^2.10.0", - "@smithy/util-config-provider": "^2.2.1", - "@smithy/util-middleware": "^2.1.2", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/core": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-1.3.3.tgz", - "integrity": "sha512-8cT/swERvU1EUMuJF914+psSeVy4+NcNhbRe1WEKN1yIMPE5+Tq5EaPq1HWjKCodcdBIyU9ViTjd62XnebXMHA==", - "dependencies": { - "@smithy/middleware-endpoint": "^2.4.2", - "@smithy/middleware-retry": "^2.1.2", - "@smithy/middleware-serde": "^2.1.2", - "@smithy/protocol-http": "^3.2.0", - "@smithy/smithy-client": "^2.4.0", - "@smithy/types": "^2.10.0", - "@smithy/util-middleware": "^2.1.2", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/credential-provider-imds": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-2.2.2.tgz", - "integrity": "sha512-a2xpqWzhzcYwImGbFox5qJLf6i5HKdVeOVj7d6kVFElmbS2QW2T4HmefRc5z1huVArk9bh5Rk1NiFp9YBCXU3g==", - "dependencies": { - "@smithy/node-config-provider": "^2.2.2", - "@smithy/property-provider": "^2.1.2", - "@smithy/types": "^2.10.0", - "@smithy/url-parser": "^2.1.2", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/eventstream-codec": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-2.1.2.tgz", - "integrity": "sha512-2PHrVRixITHSOj3bxfZmY93apGf8/DFiyhRh9W0ukfi07cvlhlRonZ0fjgcqryJjUZ5vYHqqmfIE/Qe1HM9mlw==", - "dependencies": { - "@aws-crypto/crc32": "3.0.0", - "@smithy/types": "^2.10.0", - "@smithy/util-hex-encoding": "^2.1.1", - "tslib": "^2.5.0" - } - }, - "node_modules/@smithy/fetch-http-handler": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-2.4.2.tgz", - "integrity": "sha512-sIGMVwa/8h6eqNjarI3F07gvML3mMXcqBe+BINNLuKsVKXMNBN6wRzeZbbx7lfiJDEHAP28qRns8flHEoBB7zw==", - "dependencies": { - "@smithy/protocol-http": "^3.2.0", - "@smithy/querystring-builder": "^2.1.2", - "@smithy/types": "^2.10.0", - "@smithy/util-base64": "^2.1.1", - "tslib": "^2.5.0" - } - }, - "node_modules/@smithy/hash-node": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-2.1.2.tgz", - "integrity": "sha512-3Sgn4s0g4xud1M/j6hQwYCkz04lVJ24wvCAx4xI26frr3Ao6v0o2VZkBpUySTeQbMUBp2DhuzJ0fV1zybzkckw==", - "dependencies": { - "@smithy/types": "^2.10.0", - "@smithy/util-buffer-from": "^2.1.1", - "@smithy/util-utf8": "^2.1.1", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/invalid-dependency": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-2.1.2.tgz", - "integrity": "sha512-qdgKhkFYxDJnKecx2ANwz3JRkXjm0qDgEnAs5BIfb2z/XqA2l7s9BTH7GTC/RR4E8h6EDCeb5rM2rnARxviqIg==", - "dependencies": { - "@smithy/types": "^2.10.0", - "tslib": "^2.5.0" - } - }, - "node_modules/@smithy/is-array-buffer": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.1.1.tgz", - "integrity": "sha512-xozSQrcUinPpNPNPds4S7z/FakDTh1MZWtRP/2vQtYB/u3HYrX2UXuZs+VhaKBd6Vc7g2XPr2ZtwGBNDN6fNKQ==", - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/middleware-content-length": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-2.1.2.tgz", - "integrity": "sha512-XEWtul1tHP31EtUIobEyN499paUIbnCTRtjY+ciDCEXW81lZmpjrDG3aL0FxJDPnvatVQuMV1V5eg6MCqTFaLQ==", - "dependencies": { - "@smithy/protocol-http": "^3.2.0", - "@smithy/types": "^2.10.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/middleware-endpoint": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-2.4.2.tgz", - "integrity": "sha512-72qbmVwaWcLOd/OT52fszrrlXywPwciwpsRiIk/dIvpcwkpGE9qrYZ2bt/SYcA/ma8Rz9Ni2AbBuSXLDYISS+A==", - "dependencies": { - "@smithy/middleware-serde": "^2.1.2", - "@smithy/node-config-provider": "^2.2.2", - "@smithy/shared-ini-file-loader": "^2.3.2", - "@smithy/types": "^2.10.0", - "@smithy/url-parser": "^2.1.2", - "@smithy/util-middleware": "^2.1.2", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/middleware-retry": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-2.1.2.tgz", - "integrity": "sha512-tlvSK+v9bPHHb0dLWvEaFW2Iz0IeA57ISvSaso36I33u8F8wYqo5FCvenH7TgMVBx57jyJBXOmYCZa9n5gdJIg==", - "dependencies": { - "@smithy/node-config-provider": "^2.2.2", - "@smithy/protocol-http": "^3.2.0", - "@smithy/service-error-classification": "^2.1.2", - "@smithy/smithy-client": "^2.4.0", - "@smithy/types": "^2.10.0", - "@smithy/util-middleware": "^2.1.2", - "@smithy/util-retry": "^2.1.2", - "tslib": "^2.5.0", - "uuid": "^8.3.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/middleware-retry/node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/@smithy/middleware-serde": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-2.1.2.tgz", - "integrity": "sha512-XNU6aVIhlSbjuo2XsfZ7rd4HhjTXDlNWxAmhlBfViTW1TNK02CeWdeEntp5XtQKYD//pyTIbYi35EQvIidAkOw==", - "dependencies": { - "@smithy/types": "^2.10.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/middleware-stack": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-2.1.2.tgz", - "integrity": "sha512-EPGaHGd4XmZcaRYjbhyqiqN/Q/ESxXu5e5TK24CTZUe99y8/XCxmiX8VLMM4H0DI7K3yfElR0wPAAvceoSkTgw==", - "dependencies": { - "@smithy/types": "^2.10.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/node-config-provider": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-2.2.2.tgz", - "integrity": "sha512-QXvpqHSijAm13ZsVkUo92b085UzDvYP1LblWTb3uWi9WilhDvYnVyPLXaryLhOWZ2YvdhK2170T3ZBqtg+quIQ==", - "dependencies": { - "@smithy/property-provider": "^2.1.2", - "@smithy/shared-ini-file-loader": "^2.3.2", - "@smithy/types": "^2.10.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/node-http-handler": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-2.4.0.tgz", - "integrity": "sha512-Mf2f7MMy31W8LisJ9O+7J5cKiNwBwBBLU6biQ7/sFSFdhuOxPN7hOPoZ8vlaFjvrpfOUJw9YOpjGyNTKuvomOQ==", - "dependencies": { - "@smithy/abort-controller": "^2.1.2", - "@smithy/protocol-http": "^3.2.0", - "@smithy/querystring-builder": "^2.1.2", - "@smithy/types": "^2.10.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/property-provider": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-2.1.2.tgz", - "integrity": "sha512-yaXCVFKzxbSXqOoyA7AdAgXhwdjiLeui7n2P6XLjBCz/GZFdLUJgSY6KL1PevaxT4REMwUSs/bSHAe/0jdzEHw==", - "dependencies": { - "@smithy/types": "^2.10.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/protocol-http": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-3.2.0.tgz", - "integrity": "sha512-VRp0YITYIQum+rX4zeZ3cW1wl9r90IQzQN+VLS1NxdSMt6NLsJiJqR9czTxlaeWNrLHsFAETmjmdrS48Ug1liA==", - "dependencies": { - "@smithy/types": "^2.10.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/querystring-builder": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-2.1.2.tgz", - "integrity": "sha512-wk6QpuvBBLJF5w8aADsZOtxaHY9cF5MZe1Ry3hSqqBxARdUrMoXi/jukUz5W0ftXGlbA398IN8dIIUj3WXqJXg==", - "dependencies": { - "@smithy/types": "^2.10.0", - "@smithy/util-uri-escape": "^2.1.1", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/querystring-parser": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-2.1.2.tgz", - "integrity": "sha512-z1yL5Iiagm/UxVy1tcuTFZdfOBK/QtYeK6wfClAJ7cOY7kIaYR6jn1cVXXJmhAQSh1b2ljP4xiZN4Ybj7Tbs5w==", - "dependencies": { - "@smithy/types": "^2.10.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/service-error-classification": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-2.1.2.tgz", - "integrity": "sha512-R+gL1pAPuWkH6unFridk57wDH5PFY2IlVg2NUjSAjoaIaU+sxqKf/7AOWIcx9Bdn+xY0/4IRQ69urlC+F3I9gg==", - "dependencies": { - "@smithy/types": "^2.10.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/shared-ini-file-loader": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-2.3.2.tgz", - "integrity": "sha512-idHGDJB+gBh+aaIjmWj6agmtNWftoyAenErky74hAtKyUaCvfocSBgEJ2pQ6o68svBluvGIj4NGFgJu0198mow==", - "dependencies": { - "@smithy/types": "^2.10.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/signature-v4": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-2.1.2.tgz", - "integrity": "sha512-DdPWaNGIbxzyocR3ncH8xlxQgsqteRADEdCPoivgBzwv17UzKy2obtdi2vwNc5lAJ955bGEkkWef9O7kc1Eocg==", - "dependencies": { - "@smithy/eventstream-codec": "^2.1.2", - "@smithy/is-array-buffer": "^2.1.1", - "@smithy/types": "^2.10.0", - "@smithy/util-hex-encoding": "^2.1.1", - "@smithy/util-middleware": "^2.1.2", - "@smithy/util-uri-escape": "^2.1.1", - "@smithy/util-utf8": "^2.1.1", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/smithy-client": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-2.4.0.tgz", - "integrity": "sha512-6/jxk0om9l2s9BcgHtrBn+Hd3xcFGDzxfEJ2FvGpZxIz0S7bgvZg1gyR66O1xf1w9WZBH+W7JClhfSn2gETINw==", - "dependencies": { - "@smithy/middleware-endpoint": "^2.4.2", - "@smithy/middleware-stack": "^2.1.2", - "@smithy/protocol-http": "^3.2.0", - "@smithy/types": "^2.10.0", - "@smithy/util-stream": "^2.1.2", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/types": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.10.0.tgz", - "integrity": "sha512-QYXQmpIebS8/jYXgyJjCanKZbI4Rr8tBVGBAIdDhA35f025TVjJNW69FJ0TGiDqt+lIGo037YIswq2t2Y1AYZQ==", - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/url-parser": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-2.1.2.tgz", - "integrity": "sha512-KBPi740ciTujUaY+RfQuPABD0QFmgSBN5qNVDCGTryfsbG4jkwC0YnElSzi72m24HegMyxzZDLG4Oh4/97mw2g==", - "dependencies": { - "@smithy/querystring-parser": "^2.1.2", - "@smithy/types": "^2.10.0", - "tslib": "^2.5.0" - } - }, - "node_modules/@smithy/util-base64": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-2.1.1.tgz", - "integrity": "sha512-UfHVpY7qfF/MrgndI5PexSKVTxSZIdz9InghTFa49QOvuu9I52zLPLUHXvHpNuMb1iD2vmc6R+zbv/bdMipR/g==", - "dependencies": { - "@smithy/util-buffer-from": "^2.1.1", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/util-body-length-browser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-2.1.1.tgz", - "integrity": "sha512-ekOGBLvs1VS2d1zM2ER4JEeBWAvIOUKeaFch29UjjJsxmZ/f0L3K3x0dEETgh3Q9bkZNHgT+rkdl/J/VUqSRag==", - "dependencies": { - "tslib": "^2.5.0" - } - }, - "node_modules/@smithy/util-body-length-node": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-2.2.1.tgz", - "integrity": "sha512-/ggJG+ta3IDtpNVq4ktmEUtOkH1LW64RHB5B0hcr5ZaWBmo96UX2cIOVbjCqqDickTXqBWZ4ZO0APuaPrD7Abg==", - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/util-buffer-from": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.1.1.tgz", - "integrity": "sha512-clhNjbyfqIv9Md2Mg6FffGVrJxw7bgK7s3Iax36xnfVj6cg0fUG7I4RH0XgXJF8bxi+saY5HR21g2UPKSxVCXg==", - "dependencies": { - "@smithy/is-array-buffer": "^2.1.1", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/util-config-provider": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-2.2.1.tgz", - "integrity": "sha512-50VL/tx9oYYcjJn/qKqNy7sCtpD0+s8XEBamIFo4mFFTclKMNp+rsnymD796uybjiIquB7VCB/DeafduL0y2kw==", - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/util-defaults-mode-browser": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-2.1.2.tgz", - "integrity": "sha512-YmojdmsE7VbvFGJ/8btn/5etLm1HOQkgVX6nMWlB0yBL/Vb//s3aTebUJ66zj2+LNrBS3B9S+18+LQU72Yj0AQ==", - "dependencies": { - "@smithy/property-provider": "^2.1.2", - "@smithy/smithy-client": "^2.4.0", - "@smithy/types": "^2.10.0", - "bowser": "^2.11.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@smithy/util-defaults-mode-node": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-2.2.1.tgz", - "integrity": "sha512-kof7M9Q2qP5yaQn8hHJL3KwozyvIfLe+ys7feifSul6gBAAeoraibo/MWqotb/I0fVLMlCMDwn7WXFsGUwnsew==", - "dependencies": { - "@smithy/config-resolver": "^2.1.2", - "@smithy/credential-provider-imds": "^2.2.2", - "@smithy/node-config-provider": "^2.2.2", - "@smithy/property-provider": "^2.1.2", - "@smithy/smithy-client": "^2.4.0", - "@smithy/types": "^2.10.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@smithy/util-endpoints": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-1.1.2.tgz", - "integrity": "sha512-2/REfdcJ20y9iF+9kSBRBsaoGzjT5dZ3E6/TA45GHJuJAb/vZTj76VLTcrl2iN3fWXiDK1B8RxchaLGbr7RxxA==", - "dependencies": { - "@smithy/node-config-provider": "^2.2.2", - "@smithy/types": "^2.10.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@smithy/util-hex-encoding": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-2.1.1.tgz", - "integrity": "sha512-3UNdP2pkYUUBGEXzQI9ODTDK+Tcu1BlCyDBaRHwyxhA+8xLP8agEKQq4MGmpjqb4VQAjq9TwlCQX0kP6XDKYLg==", - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/util-middleware": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-2.1.2.tgz", - "integrity": "sha512-lvSOnwQ7iAajtWb1nAyy0CkOIn8d+jGykQOtt2NXDsPzOTfejZM/Uph+O/TmVgWoXdcGuw5peUMG2f5xEIl6UQ==", - "dependencies": { - "@smithy/types": "^2.10.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/util-retry": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-2.1.2.tgz", - "integrity": "sha512-pqifOgRqwLfRu+ks3awEKKqPeYxrHLwo4Yu2EarGzeoarTd1LVEyyf5qLE6M7IiCsxnXRhn9FoWIdZOC+oC/VQ==", - "dependencies": { - "@smithy/service-error-classification": "^2.1.2", - "@smithy/types": "^2.10.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@smithy/util-stream": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-2.1.2.tgz", - "integrity": "sha512-AbGjvoSok7YeUKv9WRVRSChQfsufLR54YCAabTbaABRdIucywRQs29em0uAP6r4RLj+4aFZStWGYpFgT0P8UlQ==", - "dependencies": { - "@smithy/fetch-http-handler": "^2.4.2", - "@smithy/node-http-handler": "^2.4.0", - "@smithy/types": "^2.10.0", - "@smithy/util-base64": "^2.1.1", - "@smithy/util-buffer-from": "^2.1.1", - "@smithy/util-hex-encoding": "^2.1.1", - "@smithy/util-utf8": "^2.1.1", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/util-uri-escape": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-2.1.1.tgz", - "integrity": "sha512-saVzI1h6iRBUVSqtnlOnc9ssU09ypo7n+shdQ8hBTZno/9rZ3AuRYvoHInV57VF7Qn7B+pFJG7qTzFiHxWlWBw==", - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/util-utf8": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.1.1.tgz", - "integrity": "sha512-BqTpzYEcUMDwAKr7/mVRUtHDhs6ZoXDi9NypMvMfOr/+u1NW7JgqodPDECiiLboEm6bobcPcECxzjtQh865e9A==", - "dependencies": { - "@smithy/util-buffer-from": "^2.1.1", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/util-waiter": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-2.1.2.tgz", - "integrity": "sha512-yxLC57GBDmbDmrnH+vJxsrbV4/aYUucBONkSRLZyJIVFAl/QJH+O/h+phITHDaxVZCYZAcudYJw4ERE32BJM7g==", - "dependencies": { - "@smithy/abort-controller": "^2.1.2", - "@smithy/types": "^2.10.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@sodaru/yup-to-json-schema": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@sodaru/yup-to-json-schema/-/yup-to-json-schema-2.0.1.tgz", - "integrity": "sha512-lWb0Wiz8KZ9ip/dY1eUqt7fhTPmL24p6Hmv5Fd9pzlzAdw/YNcWZr+tiCT4oZ4Zyxzi9+1X4zv82o7jYvcFxYA==", - "dev": true - }, - "node_modules/@swc/core": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.4.2.tgz", - "integrity": "sha512-vWgY07R/eqj1/a0vsRKLI9o9klGZfpLNOVEnrv4nrccxBgYPjcf22IWwAoaBJ+wpA7Q4fVjCUM8lP0m01dpxcg==", - "hasInstallScript": true, - "dependencies": { - "@swc/counter": "^0.1.2", - "@swc/types": "^0.1.5" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/swc" - }, - "optionalDependencies": { - "@swc/core-darwin-arm64": "1.4.2", - "@swc/core-darwin-x64": "1.4.2", - "@swc/core-linux-arm-gnueabihf": "1.4.2", - "@swc/core-linux-arm64-gnu": "1.4.2", - "@swc/core-linux-arm64-musl": "1.4.2", - "@swc/core-linux-x64-gnu": "1.4.2", - "@swc/core-linux-x64-musl": "1.4.2", - "@swc/core-win32-arm64-msvc": "1.4.2", - "@swc/core-win32-ia32-msvc": "1.4.2", - "@swc/core-win32-x64-msvc": "1.4.2" - }, - "peerDependencies": { - "@swc/helpers": "^0.5.0" - }, - "peerDependenciesMeta": { - "@swc/helpers": { - "optional": true - } - } - }, - "node_modules/@swc/core-darwin-arm64": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.4.2.tgz", - "integrity": "sha512-1uSdAn1MRK5C1m/TvLZ2RDvr0zLvochgrZ2xL+lRzugLlCTlSA+Q4TWtrZaOz+vnnFVliCpw7c7qu0JouhgQIw==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-darwin-x64": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.4.2.tgz", - "integrity": "sha512-TYD28+dCQKeuxxcy7gLJUCFLqrwDZnHtC2z7cdeGfZpbI2mbfppfTf2wUPzqZk3gEC96zHd4Yr37V3Tvzar+lQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.4.2.tgz", - "integrity": "sha512-Eyqipf7ZPGj0vplKHo8JUOoU1un2sg5PjJMpEesX0k+6HKE2T8pdyeyXODN0YTFqzndSa/J43EEPXm+rHAsLFQ==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.4.2.tgz", - "integrity": "sha512-wZn02DH8VYPv3FC0ub4my52Rttsus/rFw+UUfzdb3tHMHXB66LqN+rR0ssIOZrH6K+VLN6qpTw9VizjyoH0BxA==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.4.2.tgz", - "integrity": "sha512-3G0D5z9hUj9bXNcwmA1eGiFTwe5rWkuL3DsoviTj73TKLpk7u64ND0XjEfO0huVv4vVu9H1jodrKb7nvln/dlw==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-x64-gnu": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.4.2.tgz", - "integrity": "sha512-LFxn9U8cjmYHw3jrdPNqPAkBGglKE3tCZ8rA7hYyp0BFxuo7L2ZcEnPm4RFpmSCCsExFH+LEJWuMGgWERoktvg==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-x64-musl": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.4.2.tgz", - "integrity": "sha512-dp0fAmreeVVYTUcb4u9njTPrYzKnbIH0EhH2qvC9GOYNNREUu2GezSIDgonjOXkHiTCvopG4xU7y56XtXj4VrQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.4.2.tgz", - "integrity": "sha512-HlVIiLMQkzthAdqMslQhDkoXJ5+AOLUSTV6fm6shFKZKqc/9cJvr4S8UveNERL9zUficA36yM3bbfo36McwnvQ==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.4.2.tgz", - "integrity": "sha512-WCF8faPGjCl4oIgugkp+kL9nl3nUATlzKXCEGFowMEmVVCFM0GsqlmGdPp1pjZoWc9tpYanoXQDnp5IvlDSLhA==", - "cpu": [ - "ia32" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.4.2.tgz", - "integrity": "sha512-oV71rwiSpA5xre2C5570BhCsg1HF97SNLsZ/12xv7zayGzqr3yvFALFJN8tHKpqUdCB4FGPjoP3JFdV3i+1wUw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/counter": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", - "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==" - }, - "node_modules/@swc/types": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.5.tgz", - "integrity": "sha512-myfUej5naTBWnqOCc/MdVOLVjXUXtIA+NpDrDBKJtLLg2shUjBu3cZmB/85RyitKc55+lUUyl7oRfLOvkr2hsw==" - }, - "node_modules/@szmarczak/http-timer": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", - "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", - "dependencies": { - "defer-to-connect": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@temporalio/activity": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/@temporalio/activity/-/activity-1.9.3.tgz", - "integrity": "sha512-QmY2dCNNSANhCgy4HCaeRmosyg73wI7KFbxTpnCrFjAWjxpJqBYC5qROrWHQEG/52Mcf59MtmznWSqz3M04Naw==", - "dependencies": { - "@temporalio/common": "1.9.3", - "abort-controller": "^3.0.0" - } - }, - "node_modules/@temporalio/client": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/@temporalio/client/-/client-1.9.3.tgz", - "integrity": "sha512-RgcqMqgrzQG3jOrCxnh8mYqwDqngAMKoCUOPwZZU5AAJa0sBcBLV2lEhJ9KGEavpfZej/duplUa67EV0s+M/6w==", - "dependencies": { - "@grpc/grpc-js": "~1.7.3", - "@temporalio/common": "1.9.3", - "@temporalio/proto": "1.9.3", - "abort-controller": "^3.0.0", - "long": "^5.2.3", - "uuid": "^9.0.1" - } - }, - "node_modules/@temporalio/common": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/@temporalio/common/-/common-1.9.3.tgz", - "integrity": "sha512-o6aAiqyIyu8b1bUOeY4nu04ZMwLAPR66Vtc8W/xYs7WM874wNhZlm8XWspqnmflI3W7td4y3Y50AHRi/BUNxRg==", - "dependencies": { - "@temporalio/proto": "1.9.3", - "long": "^5.2.3", - "ms": "^3.0.0-canary.1", - "proto3-json-serializer": "^2.0.0" - } - }, - "node_modules/@temporalio/common/node_modules/ms": { - "version": "3.0.0-canary.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-3.0.0-canary.1.tgz", - "integrity": "sha512-kh8ARjh8rMN7Du2igDRO9QJnqCb2xYTJxyQYK7vJJS4TvLLmsbyhiKpSW+t+y26gyOyMd0riphX0GeWKU3ky5g==", - "engines": { - "node": ">=12.13" - } - }, - "node_modules/@temporalio/core-bridge": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/@temporalio/core-bridge/-/core-bridge-1.9.3.tgz", - "integrity": "sha512-0cYVDzypc+ilpWeBsYkHP8wv/SbtcLZA4z4ZZd2c6OWKEM3+p4UuW8AiXZf/avL+rpo7cjrSRl+PTWe52vPReg==", - "hasInstallScript": true, - "dependencies": { - "@temporalio/common": "1.9.3", - "arg": "^5.0.2", - "cargo-cp-artifact": "^0.1.8", - "which": "^4.0.0" - } - }, - "node_modules/@temporalio/core-bridge/node_modules/arg": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" - }, - "node_modules/@temporalio/core-bridge/node_modules/isexe": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", - "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", - "engines": { - "node": ">=16" - } - }, - "node_modules/@temporalio/core-bridge/node_modules/which": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", - "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", - "dependencies": { - "isexe": "^3.1.1" - }, - "bin": { - "node-which": "bin/which.js" - }, - "engines": { - "node": "^16.13.0 || >=18.0.0" - } - }, - "node_modules/@temporalio/interceptors-opentelemetry": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/@temporalio/interceptors-opentelemetry/-/interceptors-opentelemetry-1.9.3.tgz", - "integrity": "sha512-XaLpTHgOcefAlwRj8CmOUvCPNM3Jr1YaezBefqXvZdZxC6a5ys/5rlEzSxuYDVCQ9tN5zQ0lbrZ8zvYdFZlgVw==", - "dependencies": { - "@opentelemetry/api": "^1.7.0", - "@opentelemetry/core": "^1.19.0", - "@opentelemetry/resources": "^1.19.0", - "@opentelemetry/sdk-trace-base": "^1.19.0" - }, - "peerDependencies": { - "@temporalio/activity": "1.9.3", - "@temporalio/client": "1.9.3", - "@temporalio/common": "1.9.3", - "@temporalio/worker": "1.9.3", - "@temporalio/workflow": "1.9.3" - } - }, - "node_modules/@temporalio/proto": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/@temporalio/proto/-/proto-1.9.3.tgz", - "integrity": "sha512-YOeelTGdz8zLX8LUlXlERvd/VTaCPmV9xN/JEUQoxsyc7YpOB2wVwdrXS7Al/2b1jOTYRm00vbn3rljRV9W5dA==", - "dependencies": { - "long": "^5.2.3", - "protobufjs": "^7.2.5" - } - }, - "node_modules/@temporalio/worker": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/@temporalio/worker/-/worker-1.9.3.tgz", - "integrity": "sha512-RFotNUeWNnLDk4EvnPVZBiYAZWc+daezr/Prn/iMsCI6e3CxbchDha6GiqIVkIF3ppq1R2Vl+FpouNA/gBniGg==", - "dependencies": { - "@swc/core": "^1.3.102", - "@temporalio/activity": "1.9.3", - "@temporalio/client": "1.9.3", - "@temporalio/common": "1.9.3", - "@temporalio/core-bridge": "1.9.3", - "@temporalio/proto": "1.9.3", - "@temporalio/workflow": "1.9.3", - "abort-controller": "^3.0.0", - "heap-js": "^2.3.0", - "memfs": "^4.6.0", - "rxjs": "^7.8.1", - "source-map": "^0.7.4", - "source-map-loader": "^4.0.2", - "swc-loader": "^0.2.3", - "unionfs": "^4.5.1", - "webpack": "^5.89.0" - }, - "engines": { - "node": ">= 14.18.0" - } - }, - "node_modules/@temporalio/worker/node_modules/source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@temporalio/workflow": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/@temporalio/workflow/-/workflow-1.9.3.tgz", - "integrity": "sha512-JU3SKZf+Cdm9ebNIbhLB3DM3NmGyusOCCZ1eSOWCHPAJo+fW9Zi6B2ba+Td7XRu6EGWJwvBwh6fnqzVCxtBPMA==", - "dependencies": { - "@temporalio/common": "1.9.3", - "@temporalio/proto": "1.9.3" - } - }, - "node_modules/@testcontainers/nats": { - "version": "10.7.1", - "resolved": "https://registry.npmjs.org/@testcontainers/nats/-/nats-10.7.1.tgz", - "integrity": "sha512-MoPsN1wI6qMR/m3+hOn7neJLdjW59ldp4w+Yr3SqB4jrZ4PmSfSchaM7a+5qdIB+BfojqzC9/Ib7P0KA6LQ/HQ==", - "dev": true, - "dependencies": { - "testcontainers": "^10.7.1" - } - }, - "node_modules/@tootallnate/once": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", - "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", - "engines": { - "node": ">= 10" - } - }, - "node_modules/@ts-morph/common": { - "version": "0.22.0", - "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.22.0.tgz", - "integrity": "sha512-HqNBuV/oIlMKdkLshXd1zKBqNQCsuPEsgQOkfFQ/eUKjRlwndXW1AjN9LVkBEIukm00gGXSRmfkl0Wv5VXLnlw==", - "dependencies": { - "fast-glob": "^3.3.2", - "minimatch": "^9.0.3", - "mkdirp": "^3.0.1", - "path-browserify": "^1.0.1" - } - }, - "node_modules/@ts-morph/common/node_modules/mkdirp": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", - "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", - "bin": { - "mkdirp": "dist/cjs/src/bin.js" - }, - "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.4", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", - "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true - }, - "node_modules/@types/accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/@types/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/amqplib": { - "version": "0.10.4", - "resolved": "https://registry.npmjs.org/@types/amqplib/-/amqplib-0.10.4.tgz", - "integrity": "sha512-Y5Sqquh/LqDxSgxYaAAFNM0M7GyONtSDCcFMJk+DQwYEjibPyW6y+Yu9H9omdkKc3epyXULmFN3GTaeBHhn2Hg==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/aws-lambda": { - "version": "8.10.122", - "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.122.tgz", - "integrity": "sha512-vBkIh9AY22kVOCEKo5CJlyCgmSWvasC+SWUxL/x/vOwRobMpI/HG1xp/Ae3AqmSiZeLUbOhW0FCD3ZjqqUxmXw==" - }, - "node_modules/@types/body-parser": { - "version": "1.19.5", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", - "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", - "dependencies": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "node_modules/@types/bunyan": { - "version": "1.8.9", - "resolved": "https://registry.npmjs.org/@types/bunyan/-/bunyan-1.8.9.tgz", - "integrity": "sha512-ZqS9JGpBxVOvsawzmVt30sP++gSQMTejCkIAQ3VdadOcRE8izTyW66hufvwLeH+YEGP6Js2AW7Gz+RMyvrEbmw==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/cacheable-request": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", - "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", - "dependencies": { - "@types/http-cache-semantics": "*", - "@types/keyv": "^3.1.4", - "@types/node": "*", - "@types/responselike": "^1.0.0" - } - }, - "node_modules/@types/caseless": { - "version": "0.12.5", - "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.5.tgz", - "integrity": "sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg==" - }, - "node_modules/@types/connect": { - "version": "3.4.36", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.36.tgz", - "integrity": "sha512-P63Zd/JUGq+PdrM1lv0Wv5SBYeA2+CORvbrXbngriYY0jzLUWfQMQQxOhjONEz/wlHOAxOdY7CY65rgQdTjq2w==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/content-disposition": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/@types/content-disposition/-/content-disposition-0.5.8.tgz", - "integrity": "sha512-QVSSvno3dE0MgO76pJhmv4Qyi/j0Yk9pBp0Y7TJ2Tlj+KCgJWY6qX7nnxCOLkZ3VYRSIk1WTxCvwUSdx6CCLdg==" - }, - "node_modules/@types/cookies": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@types/cookies/-/cookies-0.9.0.tgz", - "integrity": "sha512-40Zk8qR147RABiQ7NQnBzWzDcjKzNrntB5BAmeGCb2p/MIyOE+4BVvc17wumsUqUw00bJYqoXFHYygQnEFh4/Q==", - "dependencies": { - "@types/connect": "*", - "@types/express": "*", - "@types/keygrip": "*", - "@types/node": "*" - } - }, - "node_modules/@types/docker-modem": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@types/docker-modem/-/docker-modem-3.0.6.tgz", - "integrity": "sha512-yKpAGEuKRSS8wwx0joknWxsmLha78wNMe9R2S3UNsVOkZded8UqOrV8KoeDXoXsjndxwyF3eIhyClGbO1SEhEg==", - "dev": true, - "dependencies": { - "@types/node": "*", - "@types/ssh2": "*" - } - }, - "node_modules/@types/dockerode": { - "version": "3.3.24", - "resolved": "https://registry.npmjs.org/@types/dockerode/-/dockerode-3.3.24.tgz", - "integrity": "sha512-679y69OYusf7Fr2HtdjXPUF6hnHxSA9K4EsuagsMuPno/XpJHjXxCOy2I5YL8POnWbzjsQAi0pyKIYM9HSpQog==", - "dev": true, - "dependencies": { - "@types/docker-modem": "*", - "@types/node": "*" - } - }, - "node_modules/@types/eslint": { - "version": "8.56.3", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.3.tgz", - "integrity": "sha512-PvSf1wfv2wJpVIFUMSb+i4PvqNYkB9Rkp9ZDO3oaWzq4SKhsQk4mrMBr3ZH06I0hKrVGLBacmgl8JM4WVjb9dg==", - "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "node_modules/@types/eslint-scope": { - "version": "3.7.7", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", - "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", - "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, - "node_modules/@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" - }, - "node_modules/@types/express": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", - "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", - "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.33", - "@types/qs": "*", - "@types/serve-static": "*" - } - }, - "node_modules/@types/express-serve-static-core": { - "version": "4.17.43", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.43.tgz", - "integrity": "sha512-oaYtiBirUOPQGSWNGPWnzyAFJ0BP3cwvN4oWZQY+zUBwpVIGsKUkpBpSztp74drYcjavs7SKFZ4DX1V2QeN8rg==", - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, - "node_modules/@types/fined": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@types/fined/-/fined-1.1.5.tgz", - "integrity": "sha512-2N93vadEGDFhASTIRbizbl4bNqpMOId5zZfj6hHqYZfEzEfO9onnU4Im8xvzo8uudySDveDHBOOSlTWf38ErfQ==" - }, - "node_modules/@types/hapi__catbox": { - "version": "10.2.6", - "resolved": "https://registry.npmjs.org/@types/hapi__catbox/-/hapi__catbox-10.2.6.tgz", - "integrity": "sha512-qdMHk4fBlwRfnBBDJaoaxb+fU9Ewi2xqkXD3mNjSPl2v/G/8IJbDpVRBuIcF7oXrcE8YebU5M8cCeKh1NXEn0w==" - }, - "node_modules/@types/hapi__hapi": { - "version": "20.0.13", - "resolved": "https://registry.npmjs.org/@types/hapi__hapi/-/hapi__hapi-20.0.13.tgz", - "integrity": "sha512-LP4IPfhIO5ZPVOrJo7H8c8Slc0WYTFAUNQX1U0LBPKyXioXhH5H2TawIgxKujIyOhbwoBbpvOsBf6o5+ToJIrQ==", - "dependencies": { - "@hapi/boom": "^9.0.0", - "@hapi/iron": "^6.0.0", - "@hapi/podium": "^4.1.3", - "@types/hapi__catbox": "*", - "@types/hapi__mimos": "*", - "@types/hapi__shot": "*", - "@types/node": "*", - "joi": "^17.3.0" - } - }, - "node_modules/@types/hapi__mimos": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@types/hapi__mimos/-/hapi__mimos-4.1.4.tgz", - "integrity": "sha512-i9hvJpFYTT/qzB5xKWvDYaSXrIiNqi4ephi+5Lo6+DoQdwqPXQgmVVOZR+s3MBiHoFqsCZCX9TmVWG3HczmTEQ==", - "dependencies": { - "@types/mime-db": "*" - } - }, - "node_modules/@types/hapi__shot": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@types/hapi__shot/-/hapi__shot-4.1.6.tgz", - "integrity": "sha512-h33NBjx2WyOs/9JgcFeFhkxnioYWQAZxOHdmqDuoJ1Qjxpcs+JGvSjEEoDeWfcrF+1n47kKgqph5IpfmPOnzbg==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/http-assert": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@types/http-assert/-/http-assert-1.5.5.tgz", - "integrity": "sha512-4+tE/lwdAahgZT1g30Jkdm9PzFRde0xwxBNUyRsCitRvCQB90iuA2uJYdUnhnANRcqGXaWOGY4FEoxeElNAK2g==" - }, - "node_modules/@types/http-cache-semantics": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", - "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==" - }, - "node_modules/@types/http-errors": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", - "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==" - }, - "node_modules/@types/inquirer": { - "version": "9.0.7", - "resolved": "https://registry.npmjs.org/@types/inquirer/-/inquirer-9.0.7.tgz", - "integrity": "sha512-Q0zyBupO6NxGRZut/JdmqYKOnN95Eg5V8Csg3PGKkP+FnvsUZx1jAyK7fztIszxxMuoBA6E3KXWvdZVXIpx60g==", - "dependencies": { - "@types/through": "*", - "rxjs": "^7.2.0" - } - }, - "node_modules/@types/ioredis4": { - "name": "@types/ioredis", - "version": "4.28.10", - "resolved": "https://registry.npmjs.org/@types/ioredis/-/ioredis-4.28.10.tgz", - "integrity": "sha512-69LyhUgrXdgcNDv7ogs1qXZomnfOEnSmrmMFqKgt1XMJxmoOSG/u3wYy13yACIfKuMJ8IhKgHafDO3sx19zVQQ==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", - "dev": true - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" - }, - "node_modules/@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", - "dev": true - }, - "node_modules/@types/keygrip": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/keygrip/-/keygrip-1.0.6.tgz", - "integrity": "sha512-lZuNAY9xeJt7Bx4t4dx0rYCDqGPW8RXhQZK1td7d4H6E9zYbLoOtjBvfwdTKpsyxQI/2jv+armjX/RW+ZNpXOQ==" - }, - "node_modules/@types/keyv": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", - "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/koa": { - "version": "2.13.9", - "resolved": "https://registry.npmjs.org/@types/koa/-/koa-2.13.9.tgz", - "integrity": "sha512-tPX3cN1dGrMn+sjCDEiQqXH2AqlPoPd594S/8zxwUm/ZbPsQXKqHPUypr2gjCPhHUc+nDJLduhh5lXI/1olnGQ==", - "dependencies": { - "@types/accepts": "*", - "@types/content-disposition": "*", - "@types/cookies": "*", - "@types/http-assert": "*", - "@types/http-errors": "*", - "@types/keygrip": "*", - "@types/koa-compose": "*", - "@types/node": "*" - } - }, - "node_modules/@types/koa__router": { - "version": "12.0.3", - "resolved": "https://registry.npmjs.org/@types/koa__router/-/koa__router-12.0.3.tgz", - "integrity": "sha512-5YUJVv6NwM1z7m6FuYpKfNLTZ932Z6EF6xy2BbtpJSyn13DKNQEkXVffFVSnJHxvwwWh2SAeumpjAYUELqgjyw==", - "dependencies": { - "@types/koa": "*" - } - }, - "node_modules/@types/koa-compose": { - "version": "3.2.8", - "resolved": "https://registry.npmjs.org/@types/koa-compose/-/koa-compose-3.2.8.tgz", - "integrity": "sha512-4Olc63RY+MKvxMwVknCUDhRQX1pFQoBZ/lXcRLP69PQkEpze/0cr8LNqJQe5NFb/b19DWi2a5bTi2VAlQzhJuA==", - "dependencies": { - "@types/koa": "*" - } - }, - "node_modules/@types/liftoff": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@types/liftoff/-/liftoff-4.0.3.tgz", - "integrity": "sha512-UgbL2kR5pLrWICvr8+fuSg0u43LY250q7ZMkC+XKC3E+rs/YBDEnQIzsnhU5dYsLlwMi3R75UvCL87pObP1sxw==", - "dependencies": { - "@types/fined": "*", - "@types/node": "*" - } - }, - "node_modules/@types/linkify-it": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.5.tgz", - "integrity": "sha512-yg6E+u0/+Zjva+buc3EIb+29XEg4wltq7cSmd4Uc2EE/1nUVmxyzpX6gUXD0V8jIrG0r7YeOGVIbYRkxeooCtw==", - "dev": true - }, - "node_modules/@types/long": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", - "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==" - }, - "node_modules/@types/markdown-it": { - "version": "13.0.7", - "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-13.0.7.tgz", - "integrity": "sha512-U/CBi2YUUcTHBt5tjO2r5QV/x0Po6nsYwQU4Y04fBS6vfoImaiZ6f8bi3CjTCxBPQSO1LMyUqkByzi8AidyxfA==", - "dev": true, - "dependencies": { - "@types/linkify-it": "*", - "@types/mdurl": "*" - } - }, - "node_modules/@types/mdurl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.5.tgz", - "integrity": "sha512-6L6VymKTzYSrEf4Nev4Xa1LCHKrlTlYCBMTlQKFuddo1CvQcE52I0mwfOJayueUC7MJuXOeHTcIU683lzd0cUA==", - "dev": true - }, - "node_modules/@types/memcached": { - "version": "2.2.10", - "resolved": "https://registry.npmjs.org/@types/memcached/-/memcached-2.2.10.tgz", - "integrity": "sha512-AM9smvZN55Gzs2wRrqeMHVP7KE8KWgCJO/XL5yCly2xF6EKa4YlbpK+cLSAH4NG/Ah64HrlegmGqW8kYws7Vxg==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/mime": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", - "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==" - }, - "node_modules/@types/mime-db": { - "version": "1.43.5", - "resolved": "https://registry.npmjs.org/@types/mime-db/-/mime-db-1.43.5.tgz", - "integrity": "sha512-/bfTiIUTNPUBnwnYvUxXAre5MhD88jgagLEQiQtIASjU+bwxd8kS/ASDA4a8ufd8m0Lheu6eeMJHEUpLHoJ28A==" - }, - "node_modules/@types/minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==", - "dev": true - }, - "node_modules/@types/mysql": { - "version": "2.15.22", - "resolved": "https://registry.npmjs.org/@types/mysql/-/mysql-2.15.22.tgz", - "integrity": "sha512-wK1pzsJVVAjYCSZWQoWHziQZbNggXFDUEIGf54g4ZM/ERuP86uGdWeKZWMYlqTPMZfHJJvLPyogXGvCOg87yLQ==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/node": { - "version": "20.11.20", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.20.tgz", - "integrity": "sha512-7/rR21OS+fq8IyHTgtLkDK949uzsa6n8BkziAKtPVpugIkO6D+/ooXMvzXxDnZrmtXVfjb1bKQafYpb8s89LOg==", - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/@types/pg": { - "version": "8.6.1", - "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.6.1.tgz", - "integrity": "sha512-1Kc4oAGzAl7uqUStZCDvaLFqZrW9qWSjXOmBfdgyBP5La7Us6Mg4GBvRlSoaZMhQF/zSj1C8CtKMBkoiT8eL8w==", - "dependencies": { - "@types/node": "*", - "pg-protocol": "*", - "pg-types": "^2.2.0" - } - }, - "node_modules/@types/pg-pool": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/pg-pool/-/pg-pool-2.0.4.tgz", - "integrity": "sha512-qZAvkv1K3QbmHHFYSNRYPkRjOWRLBYrL4B9c+wG0GSVGBw0NtJwPcgx/DSddeDJvRGMHCEQ4VMEVfuJ/0gZ3XQ==", - "dependencies": { - "@types/pg": "*" - } - }, - "node_modules/@types/qs": { - "version": "6.9.11", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.11.tgz", - "integrity": "sha512-oGk0gmhnEJK4Yyk+oI7EfXsLayXatCWPHary1MtcmbAifkobT9cM9yutG/hZKIseOU0MqbIwQ/u2nn/Gb+ltuQ==" - }, - "node_modules/@types/range-parser": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", - "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==" - }, - "node_modules/@types/readable-stream": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-4.0.10.tgz", - "integrity": "sha512-AbUKBjcC8SHmImNi4yK2bbjogQlkFSg7shZCcicxPQapniOlajG8GCc39lvXzCWX4lLRRs7DM3VAeSlqmEVZUA==", - "dependencies": { - "@types/node": "*", - "safe-buffer": "~5.1.1" - } - }, - "node_modules/@types/readable-stream/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "node_modules/@types/request": { - "version": "2.48.12", - "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.12.tgz", - "integrity": "sha512-G3sY+NpsA9jnwm0ixhAFQSJ3Q9JkpLZpJbI3GMv0mIAT0y3mRabYeINzal5WOChIiaTEGQYlHOKgkaM9EisWHw==", - "dependencies": { - "@types/caseless": "*", - "@types/node": "*", - "@types/tough-cookie": "*", - "form-data": "^2.5.0" - } - }, - "node_modules/@types/responselike": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", - "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/semver": { - "version": "7.5.8", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", - "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", - "dev": true - }, - "node_modules/@types/send": { - "version": "0.17.4", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", - "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", - "dependencies": { - "@types/mime": "^1", - "@types/node": "*" - } - }, - "node_modules/@types/serve-static": { - "version": "1.15.5", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.5.tgz", - "integrity": "sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==", - "dependencies": { - "@types/http-errors": "*", - "@types/mime": "*", - "@types/node": "*" - } - }, - "node_modules/@types/shimmer": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/shimmer/-/shimmer-1.0.5.tgz", - "integrity": "sha512-9Hp0ObzwwO57DpLFF0InUjUm/II8GmKAvzbefxQTihCb7KI6yc9yzf0nLc4mVdby5N4DRCgQM2wCup9KTieeww==" - }, - "node_modules/@types/sinon": { - "version": "17.0.3", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-17.0.3.tgz", - "integrity": "sha512-j3uovdn8ewky9kRBG19bOwaZbexJu/XjtkHyjvUgt4xfPFz18dcORIMqnYh66Fx3Powhcr85NT5+er3+oViapw==", - "devOptional": true, - "dependencies": { - "@types/sinonjs__fake-timers": "*" - } - }, - "node_modules/@types/sinonjs__fake-timers": { - "version": "8.1.5", - "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.5.tgz", - "integrity": "sha512-mQkU2jY8jJEF7YHjHvsQO8+3ughTL1mcnn96igfhONmR+fUPSKIkefQYpSe8bsly2Ep7oQbn/6VG5/9/0qcArQ==", - "devOptional": true - }, - "node_modules/@types/ssh2": { - "version": "1.11.19", - "resolved": "https://registry.npmjs.org/@types/ssh2/-/ssh2-1.11.19.tgz", - "integrity": "sha512-ydbQAqEcdNVy2t1w7dMh6eWMr+iOgtEkqM/3K9RMijMaok/ER7L8GW6PwsOypHCN++M+c8S/UR9SgMqNIFstbA==", - "dev": true, - "dependencies": { - "@types/node": "^18.11.18" - } - }, - "node_modules/@types/ssh2-streams": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/@types/ssh2-streams/-/ssh2-streams-0.1.12.tgz", - "integrity": "sha512-Sy8tpEmCce4Tq0oSOYdfqaBpA3hDM8SoxoFh5vzFsu2oL+znzGz8oVWW7xb4K920yYMUY+PIG31qZnFMfPWNCg==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/ssh2/node_modules/@types/node": { - "version": "18.19.18", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.18.tgz", - "integrity": "sha512-80CP7B8y4PzZF0GWx15/gVWRrB5y/bIjNI84NK3cmQJu0WZwvmj2WMA5LcofQFVfLqqCSp545+U2LsrVzX36Zg==", - "dev": true, - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/@types/swagger-ui-dist": { - "version": "3.30.4", - "resolved": "https://registry.npmjs.org/@types/swagger-ui-dist/-/swagger-ui-dist-3.30.4.tgz", - "integrity": "sha512-FeOBc7uj4/lAIh4jkBzorvmNoUU9JgSccyDIRo0E9MJw9KQfSxlwpHCyKGnU9kfV5N5dEdfpY8wm7to3nSwTmA==", - "dev": true - }, - "node_modules/@types/tedious": { - "version": "4.0.14", - "resolved": "https://registry.npmjs.org/@types/tedious/-/tedious-4.0.14.tgz", - "integrity": "sha512-KHPsfX/FoVbUGbyYvk1q9MMQHLPeRZhRJZdO45Q4YjvFkv4hMNghCWTvy7rdKessBsmtz4euWCWAB6/tVpI1Iw==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/through": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/@types/through/-/through-0.0.33.tgz", - "integrity": "sha512-HsJ+z3QuETzP3cswwtzt2vEIiHBk/dCcHGhbmG5X3ecnwFD/lPrMpliGXxSCg03L9AhrdwA4Oz/qfspkDW+xGQ==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/tough-cookie": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", - "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==" - }, - "node_modules/@types/web-bluetooth": { - "version": "0.0.20", - "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz", - "integrity": "sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==", - "dev": true - }, - "node_modules/@types/ws": { - "version": "8.5.10", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", - "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@typeschema/core": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/@typeschema/core/-/core-0.13.1.tgz", - "integrity": "sha512-XT/hoEwhdIgBR80Ia6S8kcHBb2oaA2dKiLnZ4Aty0oVFeyd1GWNqUe0+l8TY6LdoAQXjq6+Bwfj5QXgM8IESNg==", - "peerDependencies": { - "@types/json-schema": "^7.0.15" - }, - "peerDependenciesMeta": { - "@types/json-schema": { - "optional": true - } - } - }, - "node_modules/@typeschema/yup": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/@typeschema/yup/-/yup-0.13.1.tgz", - "integrity": "sha512-CY+Z0buwvf6VB8c7F+yxBGq5jI/0N0wsonyWGMF5bfVMulquWLtWhfA/Syo70YavIOI8MVjp6XD3nWtTT9FC0Q==", - "devOptional": true, - "dependencies": { - "@typeschema/core": "0.13.1" - }, - "peerDependencies": { - "@sodaru/yup-to-json-schema": "^2.0.1", - "yup": "^1.3.3" - }, - "peerDependenciesMeta": { - "@sodaru/yup-to-json-schema": { - "optional": true - }, - "yup": { - "optional": true - } - } - }, - "node_modules/@typeschema/zod": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/@typeschema/zod/-/zod-0.13.1.tgz", - "integrity": "sha512-b8xUjmqKStB3PSMd1pQoikMLqVjcgTgQ8f2DEenXfSkgMM9AqyjLGUtQF8VOFaap07WGHHN04Nh63ObAz8EoKw==", - "dependencies": { - "@typeschema/core": "0.13.1" - }, - "peerDependencies": { - "zod": "^3.22.4", - "zod-to-json-schema": "^3.22.4" - }, - "peerDependenciesMeta": { - "zod": { - "optional": true - }, - "zod-to-json-schema": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.0.2.tgz", - "integrity": "sha512-/XtVZJtbaphtdrWjr+CJclaCVGPtOdBpFEnvtNf/jRV0IiEemRrL0qABex/nEt8isYcnFacm3nPHYQwL+Wb7qg==", - "dev": true, - "dependencies": { - "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "7.0.2", - "@typescript-eslint/type-utils": "7.0.2", - "@typescript-eslint/utils": "7.0.2", - "@typescript-eslint/visitor-keys": "7.0.2", - "debug": "^4.3.4", - "graphemer": "^1.4.0", - "ignore": "^5.2.4", - "natural-compare": "^1.4.0", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^7.0.0", - "eslint": "^8.56.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.0.2.tgz", - "integrity": "sha512-GdwfDglCxSmU+QTS9vhz2Sop46ebNCXpPPvsByK7hu0rFGRHL+AusKQJ7SoN+LbLh6APFpQwHKmDSwN35Z700Q==", - "dev": true, - "dependencies": { - "@typescript-eslint/scope-manager": "7.0.2", - "@typescript-eslint/types": "7.0.2", - "@typescript-eslint/typescript-estree": "7.0.2", - "@typescript-eslint/visitor-keys": "7.0.2", - "debug": "^4.3.4" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.56.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.0.2.tgz", - "integrity": "sha512-l6sa2jF3h+qgN2qUMjVR3uCNGjWw4ahGfzIYsCtFrQJCjhbrDPdiihYT8FnnqFwsWX+20hK592yX9I2rxKTP4g==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "7.0.2", - "@typescript-eslint/visitor-keys": "7.0.2" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.0.2.tgz", - "integrity": "sha512-IKKDcFsKAYlk8Rs4wiFfEwJTQlHcdn8CLwLaxwd6zb8HNiMcQIFX9sWax2k4Cjj7l7mGS5N1zl7RCHOVwHq2VQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/typescript-estree": "7.0.2", - "@typescript-eslint/utils": "7.0.2", - "debug": "^4.3.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.56.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/types": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.0.2.tgz", - "integrity": "sha512-ZzcCQHj4JaXFjdOql6adYV4B/oFOFjPOC9XYwCaZFRvqN8Llfvv4gSxrkQkd2u4Ci62i2c6W6gkDwQJDaRc4nA==", - "dev": true, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.0.2.tgz", - "integrity": "sha512-3AMc8khTcELFWcKcPc0xiLviEvvfzATpdPj/DXuOGIdQIIFybf4DMT1vKRbuAEOFMwhWt7NFLXRkbjsvKZQyvw==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "7.0.2", - "@typescript-eslint/visitor-keys": "7.0.2", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "minimatch": "9.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.0.2.tgz", - "integrity": "sha512-PZPIONBIB/X684bhT1XlrkjNZJIEevwkKDsdwfiu1WeqBxYEEdIgVDgm8/bbKHVu+6YOpeRqcfImTdImx/4Bsw==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.12", - "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "7.0.2", - "@typescript-eslint/types": "7.0.2", - "@typescript-eslint/typescript-estree": "7.0.2", - "semver": "^7.5.4" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.56.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.0.2.tgz", - "integrity": "sha512-8Y+YiBmqPighbm5xA2k4wKTxRzx9EkBu7Rlw+WHqMvRJ3RPz/BMBO9b2ru0LUNmXg120PHUXD5+SWFy2R8DqlQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "7.0.2", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", - "dev": true - }, - "node_modules/@uptrace/core": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/@uptrace/core/-/core-1.19.0.tgz", - "integrity": "sha512-2xWd3UmsGOBFvhdoJf11ymUT4boq8EZ6Ouo08zLEl/UzD1vetknlcUxB9Q7WP+aMFAENhyH35a87VKEufSap9g==", - "dependencies": { - "@opentelemetry/api": "~1.7.0", - "@opentelemetry/core": "~1.21.0", - "@opentelemetry/instrumentation": "~0.48.0", - "@opentelemetry/resources": "~1.21.0", - "@opentelemetry/sdk-trace-base": "~1.21.0", - "cross-fetch": "4.0.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/@uptrace/core/node_modules/@opentelemetry/instrumentation": { - "version": "0.48.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.48.0.tgz", - "integrity": "sha512-sjtZQB5PStIdCw5ovVTDGwnmQC+GGYArJNgIcydrDSqUTdYBnMrN9P4pwQZgS3vTGIp+TU1L8vMXGe51NVmIKQ==", - "dependencies": { - "@types/shimmer": "^1.0.2", - "import-in-the-middle": "1.7.1", - "require-in-the-middle": "^7.1.1", - "semver": "^7.5.2", - "shimmer": "^1.2.1" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@uptrace/node": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/@uptrace/node/-/node-1.19.0.tgz", - "integrity": "sha512-Qdg9bZYiU7HU31UyexWqUneD1HZI5Cz8f7J/lll5Qd7Nsrgtds0QMGUc/gZI8AciotqISvRs/G7QkNBGvEO83A==", - "dependencies": { - "@opentelemetry/api": "~1.7.0", - "@opentelemetry/auto-instrumentations-node": "~0.40.3", - "@opentelemetry/core": "~1.21.0", - "@opentelemetry/exporter-logs-otlp-http": "~0.48.0", - "@opentelemetry/exporter-metrics-otlp-http": "~0.48.0", - "@opentelemetry/exporter-trace-otlp-http": "~0.48.0", - "@opentelemetry/id-generator-aws-xray": "~1.2.1", - "@opentelemetry/otlp-exporter-base": "~0.48.0", - "@opentelemetry/sdk-logs": "~0.48.0", - "@opentelemetry/sdk-metrics": "~1.21.0", - "@opentelemetry/sdk-node": "~0.48.0", - "@opentelemetry/sdk-trace-base": "~1.21.0", - "@uptrace/core": "1.19.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/@vitejs/plugin-vue": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.0.4.tgz", - "integrity": "sha512-WS3hevEszI6CEVEx28F8RjTX97k3KsrcY6kvTg7+Whm5y3oYvcqzVeGCU3hxSAn4uY2CLCkeokkGKpoctccilQ==", - "dev": true, - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "peerDependencies": { - "vite": "^5.0.0", - "vue": "^3.2.25" - } - }, - "node_modules/@vitest/coverage-v8": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-1.3.1.tgz", - "integrity": "sha512-UuBnkSJUNE9rdHjDCPyJ4fYuMkoMtnghes1XohYa4At0MS3OQSAo97FrbwSLRshYsXThMZy1+ybD/byK5llyIg==", - "dev": true, - "dependencies": { - "@ampproject/remapping": "^2.2.1", - "@bcoe/v8-coverage": "^0.2.3", - "debug": "^4.3.4", - "istanbul-lib-coverage": "^3.2.2", - "istanbul-lib-report": "^3.0.1", - "istanbul-lib-source-maps": "^4.0.1", - "istanbul-reports": "^3.1.6", - "magic-string": "^0.30.5", - "magicast": "^0.3.3", - "picocolors": "^1.0.0", - "std-env": "^3.5.0", - "test-exclude": "^6.0.0", - "v8-to-istanbul": "^9.2.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "vitest": "1.3.1" - } - }, - "node_modules/@vitest/expect": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.3.1.tgz", - "integrity": "sha512-xofQFwIzfdmLLlHa6ag0dPV8YsnKOCP1KdAeVVh34vSjN2dcUiXYCD9htu/9eM7t8Xln4v03U9HLxLpPlsXdZw==", - "dev": true, - "dependencies": { - "@vitest/spy": "1.3.1", - "@vitest/utils": "1.3.1", - "chai": "^4.3.10" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/runner": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.3.1.tgz", - "integrity": "sha512-5FzF9c3jG/z5bgCnjr8j9LNq/9OxV2uEBAITOXfoe3rdZJTdO7jzThth7FXv/6b+kdY65tpRQB7WaKhNZwX+Kg==", - "dev": true, - "dependencies": { - "@vitest/utils": "1.3.1", - "p-limit": "^5.0.0", - "pathe": "^1.1.1" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/runner/node_modules/p-limit": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-5.0.0.tgz", - "integrity": "sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==", - "dev": true, - "dependencies": { - "yocto-queue": "^1.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@vitest/runner/node_modules/yocto-queue": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", - "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", - "dev": true, - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@vitest/snapshot": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.3.1.tgz", - "integrity": "sha512-EF++BZbt6RZmOlE3SuTPu/NfwBF6q4ABS37HHXzs2LUVPBLx2QoY/K0fKpRChSo8eLiuxcbCVfqKgx/dplCDuQ==", - "dev": true, - "dependencies": { - "magic-string": "^0.30.5", - "pathe": "^1.1.1", - "pretty-format": "^29.7.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/spy": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.3.1.tgz", - "integrity": "sha512-xAcW+S099ylC9VLU7eZfdT9myV67Nor9w9zhf0mGCYJSO+zM2839tOeROTdikOi/8Qeusffvxb/MyBSOja1Uig==", - "dev": true, - "dependencies": { - "tinyspy": "^2.2.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/utils": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.3.1.tgz", - "integrity": "sha512-d3Waie/299qqRyHTm2DjADeTaNdNSVsnwHPWrs20JMpjh6eiVq7ggggweO8rc4arhf6rRkWuHKwvxGvejUXZZQ==", - "dev": true, - "dependencies": { - "diff-sequences": "^29.6.3", - "estree-walker": "^3.0.3", - "loupe": "^2.3.7", - "pretty-format": "^29.7.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vue/compiler-core": { - "version": "3.4.19", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.19.tgz", - "integrity": "sha512-gj81785z0JNzRcU0Mq98E56e4ltO1yf8k5PQ+tV/7YHnbZkrM0fyFyuttnN8ngJZjbpofWE/m4qjKBiLl8Ju4w==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.23.9", - "@vue/shared": "3.4.19", - "entities": "^4.5.0", - "estree-walker": "^2.0.2", - "source-map-js": "^1.0.2" - } - }, - "node_modules/@vue/compiler-core/node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "dev": true - }, - "node_modules/@vue/compiler-dom": { - "version": "3.4.19", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.19.tgz", - "integrity": "sha512-vm6+cogWrshjqEHTzIDCp72DKtea8Ry/QVpQRYoyTIg9k7QZDX6D8+HGURjtmatfgM8xgCFtJJaOlCaRYRK3QA==", - "dev": true, - "dependencies": { - "@vue/compiler-core": "3.4.19", - "@vue/shared": "3.4.19" - } - }, - "node_modules/@vue/compiler-sfc": { - "version": "3.4.19", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.19.tgz", - "integrity": "sha512-LQ3U4SN0DlvV0xhr1lUsgLCYlwQfUfetyPxkKYu7dkfvx7g3ojrGAkw0AERLOKYXuAGnqFsEuytkdcComei3Yg==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.23.9", - "@vue/compiler-core": "3.4.19", - "@vue/compiler-dom": "3.4.19", - "@vue/compiler-ssr": "3.4.19", - "@vue/shared": "3.4.19", - "estree-walker": "^2.0.2", - "magic-string": "^0.30.6", - "postcss": "^8.4.33", - "source-map-js": "^1.0.2" - } - }, - "node_modules/@vue/compiler-sfc/node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "dev": true - }, - "node_modules/@vue/compiler-ssr": { - "version": "3.4.19", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.19.tgz", - "integrity": "sha512-P0PLKC4+u4OMJ8sinba/5Z/iDT84uMRRlrWzadgLA69opCpI1gG4N55qDSC+dedwq2fJtzmGald05LWR5TFfLw==", - "dev": true, - "dependencies": { - "@vue/compiler-dom": "3.4.19", - "@vue/shared": "3.4.19" - } - }, - "node_modules/@vue/devtools-api": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.0.15.tgz", - "integrity": "sha512-kgEYWosDyWpS1vFSuJNNWUnHkP+VkL3Y+9mw+rf7ex41SwbYL/WdC3KXqAtjiSrEs7r/FrHmUTh0BkINJPFkbA==", - "dev": true, - "dependencies": { - "@vue/devtools-kit": "^7.0.15" - } - }, - "node_modules/@vue/devtools-kit": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.0.15.tgz", - "integrity": "sha512-dT7OeCe1LUCIhHIb/yRR6Hn+XHh73r1o78onqCrxEKHdoZwBItiIeVnmJZPEUDFstIxfs+tJL231mySk3laTow==", - "dev": true, - "dependencies": { - "@vue/devtools-shared": "^7.0.15", - "hookable": "^5.5.3", - "mitt": "^3.0.1", - "perfect-debounce": "^1.0.0", - "speakingurl": "^14.0.1" - }, - "peerDependencies": { - "vue": "^3.0.0" - } - }, - "node_modules/@vue/devtools-shared": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.0.15.tgz", - "integrity": "sha512-fpfvMVvS7aDgO7x2JPFiTQ1MHcCc63/bE7yTgs278gMBybuO9b3hdiZ/k0Pw1rN+RefaU9yQiFA+5CCFc1D+6w==", - "dev": true, - "dependencies": { - "rfdc": "^1.3.1" - } - }, - "node_modules/@vue/reactivity": { - "version": "3.4.19", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.19.tgz", - "integrity": "sha512-+VcwrQvLZgEclGZRHx4O2XhyEEcKaBi50WbxdVItEezUf4fqRh838Ix6amWTdX0CNb/b6t3Gkz3eOebfcSt+UA==", - "dev": true, - "dependencies": { - "@vue/shared": "3.4.19" - } - }, - "node_modules/@vue/runtime-core": { - "version": "3.4.19", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.19.tgz", - "integrity": "sha512-/Z3tFwOrerJB/oyutmJGoYbuoadphDcJAd5jOuJE86THNZji9pYjZroQ2NFsZkTxOq0GJbb+s2kxTYToDiyZzw==", - "dev": true, - "dependencies": { - "@vue/reactivity": "3.4.19", - "@vue/shared": "3.4.19" - } - }, - "node_modules/@vue/runtime-dom": { - "version": "3.4.19", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.19.tgz", - "integrity": "sha512-IyZzIDqfNCF0OyZOauL+F4yzjMPN2rPd8nhqPP2N1lBn3kYqJpPHHru+83Rkvo2lHz5mW+rEeIMEF9qY3PB94g==", - "dev": true, - "dependencies": { - "@vue/runtime-core": "3.4.19", - "@vue/shared": "3.4.19", - "csstype": "^3.1.3" - } - }, - "node_modules/@vue/server-renderer": { - "version": "3.4.19", - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.19.tgz", - "integrity": "sha512-eAj2p0c429RZyyhtMRnttjcSToch+kTWxFPHlzGMkR28ZbF1PDlTcmGmlDxccBuqNd9iOQ7xPRPAGgPVj+YpQw==", - "dev": true, - "dependencies": { - "@vue/compiler-ssr": "3.4.19", - "@vue/shared": "3.4.19" - }, - "peerDependencies": { - "vue": "3.4.19" - } - }, - "node_modules/@vue/shared": { - "version": "3.4.19", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.19.tgz", - "integrity": "sha512-/KliRRHMF6LoiThEy+4c1Z4KB/gbPrGjWwJR+crg2otgrf/egKzRaCPvJ51S5oetgsgXLfc4Rm5ZgrKHZrtMSw==", - "dev": true - }, - "node_modules/@vueuse/core": { - "version": "10.8.0", - "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-10.8.0.tgz", - "integrity": "sha512-G9Ok9fjx10TkNIPn8V1dJmK1NcdJCtYmDRyYiTMUyJ1p0Tywc1zmOoCQ2xhHYyz8ULBU4KjIJQ9n+Lrty74iVw==", - "dev": true, - "dependencies": { - "@types/web-bluetooth": "^0.0.20", - "@vueuse/metadata": "10.8.0", - "@vueuse/shared": "10.8.0", - "vue-demi": ">=0.14.7" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/@vueuse/core/node_modules/vue-demi": { - "version": "0.14.7", - "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.7.tgz", - "integrity": "sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==", - "dev": true, - "hasInstallScript": true, - "bin": { - "vue-demi-fix": "bin/vue-demi-fix.js", - "vue-demi-switch": "bin/vue-demi-switch.js" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - }, - "peerDependencies": { - "@vue/composition-api": "^1.0.0-rc.1", - "vue": "^3.0.0-0 || ^2.6.0" - }, - "peerDependenciesMeta": { - "@vue/composition-api": { - "optional": true - } - } - }, - "node_modules/@vueuse/integrations": { - "version": "10.8.0", - "resolved": "https://registry.npmjs.org/@vueuse/integrations/-/integrations-10.8.0.tgz", - "integrity": "sha512-sw3P/7cXOfNLQfERp7P0IJ2ODjLE2C3BGXpBQJQkS309c1jbJak9yu4EnY70WaZjkj53aeWSFU6BbHrUxXJ7SA==", - "dev": true, - "dependencies": { - "@vueuse/core": "10.8.0", - "@vueuse/shared": "10.8.0", - "vue-demi": ">=0.14.7" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - }, - "peerDependencies": { - "async-validator": "*", - "axios": "*", - "change-case": "*", - "drauu": "*", - "focus-trap": "*", - "fuse.js": "*", - "idb-keyval": "*", - "jwt-decode": "*", - "nprogress": "*", - "qrcode": "*", - "sortablejs": "*", - "universal-cookie": "*" - }, - "peerDependenciesMeta": { - "async-validator": { - "optional": true - }, - "axios": { - "optional": true - }, - "change-case": { - "optional": true - }, - "drauu": { - "optional": true - }, - "focus-trap": { - "optional": true - }, - "fuse.js": { - "optional": true - }, - "idb-keyval": { - "optional": true - }, - "jwt-decode": { - "optional": true - }, - "nprogress": { - "optional": true - }, - "qrcode": { - "optional": true - }, - "sortablejs": { - "optional": true - }, - "universal-cookie": { - "optional": true - } - } - }, - "node_modules/@vueuse/integrations/node_modules/vue-demi": { - "version": "0.14.7", - "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.7.tgz", - "integrity": "sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==", - "dev": true, - "hasInstallScript": true, - "bin": { - "vue-demi-fix": "bin/vue-demi-fix.js", - "vue-demi-switch": "bin/vue-demi-switch.js" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - }, - "peerDependencies": { - "@vue/composition-api": "^1.0.0-rc.1", - "vue": "^3.0.0-0 || ^2.6.0" - }, - "peerDependenciesMeta": { - "@vue/composition-api": { - "optional": true - } - } - }, - "node_modules/@vueuse/metadata": { - "version": "10.8.0", - "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-10.8.0.tgz", - "integrity": "sha512-Nim/Vle5OgXcXhAvGOgkJQXB1Yb+Kq/fMbLuv3YYDYbiQrwr39ljuD4k9fPeq4yUyokYRo2RaNQmbbIMWB/9+w==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/@vueuse/shared": { - "version": "10.8.0", - "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-10.8.0.tgz", - "integrity": "sha512-dUdy6zwHhULGxmr9YUg8e+EnB39gcM4Fe2oKBSrh3cOsV30JcMPtsyuspgFCUo5xxFNaeMf/W2yyKfST7Bg8oQ==", - "dev": true, - "dependencies": { - "vue-demi": ">=0.14.7" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/@vueuse/shared/node_modules/vue-demi": { - "version": "0.14.7", - "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.7.tgz", - "integrity": "sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==", - "dev": true, - "hasInstallScript": true, - "bin": { - "vue-demi-fix": "bin/vue-demi-fix.js", - "vue-demi-switch": "bin/vue-demi-switch.js" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - }, - "peerDependencies": { - "@vue/composition-api": "^1.0.0-rc.1", - "vue": "^3.0.0-0 || ^2.6.0" - }, - "peerDependenciesMeta": { - "@vue/composition-api": { - "optional": true - } - } - }, - "node_modules/@webassemblyjs/ast": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz", - "integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==", - "dependencies": { - "@webassemblyjs/helper-numbers": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6" - } - }, - "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", - "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==" - }, - "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", - "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==" - }, - "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz", - "integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==" - }, - "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", - "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", - "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.11.6", - "@webassemblyjs/helper-api-error": "1.11.6", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", - "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==" - }, - "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz", - "integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==", - "dependencies": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-buffer": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/wasm-gen": "1.11.6" - } - }, - "node_modules/@webassemblyjs/ieee754": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", - "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", - "dependencies": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "node_modules/@webassemblyjs/leb128": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", - "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", - "dependencies": { - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/utf8": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", - "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==" - }, - "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz", - "integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==", - "dependencies": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-buffer": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/helper-wasm-section": "1.11.6", - "@webassemblyjs/wasm-gen": "1.11.6", - "@webassemblyjs/wasm-opt": "1.11.6", - "@webassemblyjs/wasm-parser": "1.11.6", - "@webassemblyjs/wast-printer": "1.11.6" - } - }, - "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz", - "integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==", - "dependencies": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/ieee754": "1.11.6", - "@webassemblyjs/leb128": "1.11.6", - "@webassemblyjs/utf8": "1.11.6" - } - }, - "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz", - "integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==", - "dependencies": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-buffer": "1.11.6", - "@webassemblyjs/wasm-gen": "1.11.6", - "@webassemblyjs/wasm-parser": "1.11.6" - } - }, - "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz", - "integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==", - "dependencies": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-api-error": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/ieee754": "1.11.6", - "@webassemblyjs/leb128": "1.11.6", - "@webassemblyjs/utf8": "1.11.6" - } - }, - "node_modules/@webassemblyjs/wast-printer": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz", - "integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==", - "dependencies": { - "@webassemblyjs/ast": "1.11.6", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==" - }, - "node_modules/@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" - }, - "node_modules/abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "dependencies": { - "event-target-shim": "^5.0.0" - }, - "engines": { - "node": ">=6.5" - } - }, - "node_modules/abstract-logging": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz", - "integrity": "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==" - }, - "node_modules/acorn": { - "version": "8.11.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", - "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-import-assertions": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", - "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", - "peerDependencies": { - "acorn": "^8" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/acorn-walk": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", - "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/aggregate-error": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-4.0.1.tgz", - "integrity": "sha512-0poP0T7el6Vq3rstR8Mn4V/IQrpBLO6POkUSrN7RhyY+GF/InCFShQzsQ39T25gkHhLgSLByyAz+Kjb+c2L98w==", - "dependencies": { - "clean-stack": "^4.0.0", - "indent-string": "^5.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/ajv-formats/node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, - "node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "peerDependencies": { - "ajv": "^6.9.1" - } - }, - "node_modules/algoliasearch": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-4.22.1.tgz", - "integrity": "sha512-jwydKFQJKIx9kIZ8Jm44SdpigFwRGPESaxZBaHSV0XWN2yBJAOT4mT7ppvlrpA4UGzz92pqFnVKr/kaZXrcreg==", - "dev": true, - "dependencies": { - "@algolia/cache-browser-local-storage": "4.22.1", - "@algolia/cache-common": "4.22.1", - "@algolia/cache-in-memory": "4.22.1", - "@algolia/client-account": "4.22.1", - "@algolia/client-analytics": "4.22.1", - "@algolia/client-common": "4.22.1", - "@algolia/client-personalization": "4.22.1", - "@algolia/client-search": "4.22.1", - "@algolia/logger-common": "4.22.1", - "@algolia/logger-console": "4.22.1", - "@algolia/requester-browser-xhr": "4.22.1", - "@algolia/requester-common": "4.22.1", - "@algolia/requester-node-http": "4.22.1", - "@algolia/transporter": "4.22.1" - } - }, - "node_modules/amqplib": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/amqplib/-/amqplib-0.10.3.tgz", - "integrity": "sha512-UHmuSa7n8vVW/a5HGh2nFPqAEr8+cD4dEZ6u9GjP91nHfr1a54RyAKyra7Sb5NH7NBKOUlyQSMXIp0qAixKexw==", - "dependencies": { - "@acuminous/bitsyntax": "^0.1.2", - "buffer-more-ints": "~1.0.0", - "readable-stream": "1.x >=1.1.9", - "url-parse": "~1.5.10" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/amqplib/node_modules/isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" - }, - "node_modules/amqplib/node_modules/readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "node_modules/amqplib/node_modules/string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==" - }, - "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==", - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-escapes/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==", - "engines": { - "node": ">=10" - }, - "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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-sequence-parser": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ansi-sequence-parser/-/ansi-sequence-parser-1.1.1.tgz", - "integrity": "sha512-vJXt3yiaUL4UU546s3rPXlsry/RnM730G1+HkpKE012AN0sx1eOrxSu95oKDIonskeLTijMgqWZ3uDEe3NFvyg==", - "dev": true - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "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/archiver": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.2.tgz", - "integrity": "sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==", - "dev": true, - "dependencies": { - "archiver-utils": "^2.1.0", - "async": "^3.2.4", - "buffer-crc32": "^0.2.1", - "readable-stream": "^3.6.0", - "readdir-glob": "^1.1.2", - "tar-stream": "^2.2.0", - "zip-stream": "^4.1.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/archiver-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz", - "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==", - "dev": true, - "dependencies": { - "glob": "^7.1.4", - "graceful-fs": "^4.2.0", - "lazystream": "^1.0.0", - "lodash.defaults": "^4.2.0", - "lodash.difference": "^4.5.0", - "lodash.flatten": "^4.4.0", - "lodash.isplainobject": "^4.0.6", - "lodash.union": "^4.6.0", - "normalize-path": "^3.0.0", - "readable-stream": "^2.0.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/archiver-utils/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/archiver-utils/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/archiver-utils/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true - }, - "node_modules/archiver-utils/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/archiver-utils/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/archiver-utils/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "node_modules/archiver-utils/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/archy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==" - }, - "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": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/array-buffer-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", - "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.5", - "is-array-buffer": "^3.0.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array-each": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", - "integrity": "sha512-zHjL5SZa68hkKHBFBK6DJCTtr9sfTCPCaph/L7tMSLcTFgy+zX7E+6q5UArbtOtMBCtxdICpfTCspRse+ywyXA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/array-includes": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.7.tgz", - "integrity": "sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", - "is-string": "^1.0.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array-slice": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz", - "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/array.prototype.filter": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array.prototype.filter/-/array.prototype.filter-1.0.3.tgz", - "integrity": "sha512-VizNcj/RGJiUyQBgzwxzE5oHdeuXY5hSbbmKMlphj1cy1Vl7Pn2asCGbSrru6hSQjmCzqTBPVWAF/whmEOVHbw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-array-method-boxes-properly": "^1.0.0", - "is-string": "^1.0.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.findlastindex": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.4.tgz", - "integrity": "sha512-hzvSHUshSpCflDR1QMUBLHGHP1VIEBegT4pix9H/Z92Xw3ySoy6c2qh7lJWTJnRJ8JCZ9bJNCgTyYaJGcJu6xQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", - "es-errors": "^1.3.0", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.flat": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", - "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.flatmap": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", - "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", - "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", - "dev": true, - "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", - "es-errors": "^1.2.1", - "get-intrinsic": "^1.2.3", - "is-array-buffer": "^3.0.4", - "is-shared-array-buffer": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/asn1": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", - "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", - "dev": true, - "dependencies": { - "safer-buffer": "~2.1.0" - } - }, - "node_modules/assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/async": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", - "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", - "dev": true - }, - "node_modules/async-lock": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/async-lock/-/async-lock-1.4.1.tgz", - "integrity": "sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==", - "dev": true - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" - }, - "node_modules/atomic-sleep": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", - "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/available-typed-arrays": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", - "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", - "dependencies": { - "possible-typed-array-names": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/avvio": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/avvio/-/avvio-8.3.0.tgz", - "integrity": "sha512-VBVH0jubFr9LdFASy/vNtm5giTrnbVquWBhT0fyizuNK2rQ7e7ONU2plZQWUNqtE1EmxFEb+kbSkFRkstiaS9Q==", - "dependencies": { - "@fastify/error": "^3.3.0", - "archy": "^1.0.0", - "debug": "^4.0.0", - "fastq": "^1.17.1" - } - }, - "node_modules/b4a": { - "version": "1.6.6", - "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.6.tgz", - "integrity": "sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==", - "dev": true - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "node_modules/bare-events": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.2.0.tgz", - "integrity": "sha512-Yyyqff4PIFfSuthCZqLlPISTWHmnQxoPuAvkmgzsJEmG3CesdIv6Xweayl0JkCZJSB2yYIdJyEz97tpxNhgjbg==", - "dev": true, - "optional": true - }, - "node_modules/bare-fs": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-2.2.0.tgz", - "integrity": "sha512-+VhW202E9eTVGkX7p+TNXtZC4RTzj9JfJW7PtfIbZ7mIQ/QT9uOafQTx7lx2n9ERmWsXvLHF4hStAFn4gl2mQw==", - "dev": true, - "optional": true, - "dependencies": { - "bare-events": "^2.0.0", - "bare-os": "^2.0.0", - "bare-path": "^2.0.0", - "streamx": "^2.13.0" - } - }, - "node_modules/bare-os": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-2.2.0.tgz", - "integrity": "sha512-hD0rOPfYWOMpVirTACt4/nK8mC55La12K5fY1ij8HAdfQakD62M+H4o4tpfKzVGLgRDTuk3vjA4GqGXXCeFbag==", - "dev": true, - "optional": true - }, - "node_modules/bare-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-2.1.0.tgz", - "integrity": "sha512-DIIg7ts8bdRKwJRJrUMy/PICEaQZaPGZ26lsSx9MJSwIhSrcdHn7/C8W+XmnG/rKi6BaRcz+JO00CjZteybDtw==", - "dev": true, - "optional": true, - "dependencies": { - "bare-os": "^2.1.0" - } - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "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/bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", - "dev": true, - "dependencies": { - "tweetnacl": "^0.14.3" - } - }, - "node_modules/bignumber.js": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", - "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==", - "engines": { - "node": "*" - } - }, - "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/bowser": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", - "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==" - }, - "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.23.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", - "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "caniuse-lite": "^1.0.30001587", - "electron-to-chromium": "^1.4.668", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.13" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "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": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/buffer-crc32": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/buffer-equal-constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" - }, - "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==" - }, - "node_modules/buffer-more-ints": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-more-ints/-/buffer-more-ints-1.0.0.tgz", - "integrity": "sha512-EMetuGFz5SLsT0QTnXzINh4Ksr+oo4i+UGTXEshiGCQWnsgSs7ZhJ8fzlwQ+OzEMs0MpDAMr1hxnblp5a4vcHg==" - }, - "node_modules/buildcheck": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/buildcheck/-/buildcheck-0.0.6.tgz", - "integrity": "sha512-8f9ZJCUXyT1M35Jx7MkBgmBMo3oHTTBIPLiY9xyL0pl3T5RwcPEY8cUHr5LBNfu/fk6c2T4DJZuVM/8ZZT2D2A==", - "dev": true, - "optional": true, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/builtin-modules": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", - "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", - "dev": true, - "peer": true, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/builtins": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz", - "integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==", - "dev": true, - "peer": true, - "dependencies": { - "semver": "^7.0.0" - } - }, - "node_modules/byline": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/byline/-/byline-5.0.0.tgz", - "integrity": "sha512-s6webAy+R4SR8XVuJWt2V2rGvhnrhxN+9S15GNuTK3wKPOXFF6RNc+8ug2XhH+2s4f+uudG4kUVYmYOQWL2g0Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/cac": { - "version": "6.7.14", - "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", - "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/cacheable-lookup": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", - "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", - "engines": { - "node": ">=10.6.0" - } - }, - "node_modules/cacheable-request": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", - "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", - "dependencies": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^4.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^6.0.1", - "responselike": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cacheable-request/node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/call-bind": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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/camel-case": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", - "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", - "dependencies": { - "pascal-case": "^3.1.2", - "tslib": "^2.0.3" - } - }, - "node_modules/camelcase": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-8.0.0.tgz", - "integrity": "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001589", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001589.tgz", - "integrity": "sha512-vNQWS6kI+q6sBlHbh71IIeC+sRwK2N3EDySc/updIGhIee2x5z00J4c1242/5/d6EpEMdOnk/m+6tuk4/tcsqg==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ] - }, - "node_modules/capital-case": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/capital-case/-/capital-case-1.0.4.tgz", - "integrity": "sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==", - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3", - "upper-case-first": "^2.0.2" - } - }, - "node_modules/cargo-cp-artifact": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/cargo-cp-artifact/-/cargo-cp-artifact-0.1.8.tgz", - "integrity": "sha512-3j4DaoTrsCD1MRkTF2Soacii0Nx7UHCce0EwUf4fHnggwiE4fbmF2AbnfzayR36DF8KGadfh7M/Yfy625kgPlA==", - "bin": { - "cargo-cp-artifact": "bin/cargo-cp-artifact.js" - } - }, - "node_modules/chai": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.4.1.tgz", - "integrity": "sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==", - "dev": true, - "dependencies": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.3", - "deep-eql": "^4.1.3", - "get-func-name": "^2.0.2", - "loupe": "^2.3.6", - "pathval": "^1.1.1", - "type-detect": "^4.0.8" - }, - "engines": { - "node": ">=4" - } - }, - "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/change-case": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/change-case/-/change-case-4.1.2.tgz", - "integrity": "sha512-bSxY2ws9OtviILG1EiY5K7NNxkqg/JnRnFxLtKQ96JaviiIxi7djMrSd0ECT9AC+lttClmYwKw53BWpOMblo7A==", - "dependencies": { - "camel-case": "^4.1.2", - "capital-case": "^1.0.4", - "constant-case": "^3.0.4", - "dot-case": "^3.0.4", - "header-case": "^2.0.4", - "no-case": "^3.0.4", - "param-case": "^3.0.4", - "pascal-case": "^3.1.2", - "path-case": "^3.0.4", - "sentence-case": "^3.0.4", - "snake-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" - }, - "node_modules/check-error": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", - "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", - "dev": true, - "dependencies": { - "get-func-name": "^2.0.2" - }, - "engines": { - "node": "*" - } - }, - "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chokidar/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/chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "dev": true - }, - "node_modules/chrome-trace-event": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", - "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", - "engines": { - "node": ">=6.0" - } - }, - "node_modules/cjs-module-lexer": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", - "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==" - }, - "node_modules/clean-stack": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-4.2.0.tgz", - "integrity": "sha512-LYv6XPxoyODi36Dp976riBtSY27VmFo+MKqEU9QCCWyTrdEPDog+RWA7xQWHi6Vbp61j5c4cdzzX1NidnwtUWg==", - "dependencies": { - "escape-string-regexp": "5.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/clean-stack/node_modules/escape-string-regexp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dependencies": { - "restore-cursor": "^3.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-spinners": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", - "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-width": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", - "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", - "engines": { - "node": ">= 12" - } - }, - "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==", - "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==" - }, - "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==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/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==", - "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/clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/clone-response": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", - "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", - "dependencies": { - "mimic-response": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cloudevents": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/cloudevents/-/cloudevents-8.0.0.tgz", - "integrity": "sha512-G1Z/r8QMFAsP+F1PuZSHzx1ocPy4vrdQMTHD3orjDaM5kccmPU6nMmpVrF07b53aaxcrLbORUmRepY/DgvdhVw==", - "dependencies": { - "ajv": "^8.11.0", - "ajv-formats": "^2.1.1", - "json-bigint": "^1.0.0", - "process": "^0.11.10", - "util": "^0.12.4", - "uuid": "^8.3.2" - }, - "engines": { - "node": ">=16 <=20" - } - }, - "node_modules/cloudevents/node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/cloudevents/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, - "node_modules/cloudevents/node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/cluster-key-slot": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", - "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", - "engines": { - "node": ">=0.10.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==" - }, - "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==", - "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==" - }, - "node_modules/colorette": { - "version": "2.0.20", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", - "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==" - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/commander": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", - "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", - "engines": { - "node": ">=14" - } - }, - "node_modules/commist": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/commist/-/commist-3.2.0.tgz", - "integrity": "sha512-4PIMoPniho+LqXmpS5d3NuGYncG6XWlkBSVGiWycL22dd42OYdUGil2CWuzklaJoNxyxUSpO4MKIBU94viWNAw==" - }, - "node_modules/compress-commons": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.2.tgz", - "integrity": "sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==", - "dev": true, - "dependencies": { - "buffer-crc32": "^0.2.13", - "crc32-stream": "^4.0.2", - "normalize-path": "^3.0.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" - }, - "node_modules/concat-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", - "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", - "engines": [ - "node >= 6.0" - ], - "dependencies": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.0.2", - "typedarray": "^0.0.6" - } - }, - "node_modules/constant-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/constant-case/-/constant-case-3.0.4.tgz", - "integrity": "sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ==", - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3", - "upper-case": "^2.0.2" - } - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "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/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" - }, - "node_modules/cpu-features": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.9.tgz", - "integrity": "sha512-AKjgn2rP2yJyfbepsmLfiYcmtNn/2eUvocUyM/09yB0YDiz39HteK/5/T4Onf0pmdYDMgkBoGvRLvEguzyL7wQ==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "dependencies": { - "buildcheck": "~0.0.6", - "nan": "^2.17.0" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/crc-32": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", - "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", - "dev": true, - "bin": { - "crc32": "bin/crc32.njs" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/crc32-stream": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.3.tgz", - "integrity": "sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==", - "dev": true, - "dependencies": { - "crc-32": "^1.2.0", - "readable-stream": "^3.4.0" - }, - "engines": { - "node": ">= 10" - } - }, - "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-fetch": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", - "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", - "dependencies": { - "node-fetch": "^2.6.12" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "dev": true - }, - "node_modules/date-fns": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.3.1.tgz", - "integrity": "sha512-y8e109LYGgoQDveiEBD3DYXKba1jWf5BA8YU1FL5Tvm0BTdEfy54WLCwnuYWZNnzzvALy/QQ4Hov+Q9RVRv+Zw==", - "dev": true, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/kossnocorp" - } - }, - "node_modules/dateformat": { - "version": "4.6.3", - "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz", - "integrity": "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==", - "engines": { - "node": "*" - } - }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "dependencies": { - "mimic-response": "^3.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/decompress-response/node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/deep-eql": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", - "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", - "dev": true, - "dependencies": { - "type-detect": "^4.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "node_modules/defaults": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", - "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", - "dependencies": { - "clone": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/defer-to-connect": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", - "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", - "engines": { - "node": ">=10" - } - }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/define-lazy-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", - "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", - "engines": { - "node": ">=8" - } - }, - "node_modules/define-properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "dev": true, - "dependencies": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/del": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/del/-/del-7.1.0.tgz", - "integrity": "sha512-v2KyNk7efxhlyHpjEvfyxaAihKKK0nWCuf6ZtqZcFFpQRG0bJ12Qsr0RpvsICMjAAZ8DOVCxrlqpxISlMHC4Kg==", - "dependencies": { - "globby": "^13.1.2", - "graceful-fs": "^4.2.10", - "is-glob": "^4.0.3", - "is-path-cwd": "^3.0.0", - "is-path-inside": "^4.0.0", - "p-map": "^5.5.0", - "rimraf": "^3.0.2", - "slash": "^4.0.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/del/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==", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/del/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "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/del/node_modules/globby": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/globby/-/globby-13.2.2.tgz", - "integrity": "sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==", - "dependencies": { - "dir-glob": "^3.0.1", - "fast-glob": "^3.3.0", - "ignore": "^5.2.4", - "merge2": "^1.4.1", - "slash": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/del/node_modules/is-path-inside": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-4.0.0.tgz", - "integrity": "sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/del/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/del/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/del/node_modules/slash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", - "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/detect-file": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", - "integrity": "sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/diff": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", - "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/diff-sequences": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", - "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/docker-compose": { - "version": "0.24.6", - "resolved": "https://registry.npmjs.org/docker-compose/-/docker-compose-0.24.6.tgz", - "integrity": "sha512-VidlUyNzXMaVsuM79sjSvwC4nfojkP2VneL+Zfs538M2XFnffZDhx6veqnz/evCNIYGyz5O+1fgL6+g0NLWTBA==", - "dev": true, - "dependencies": { - "yaml": "^2.2.2" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/docker-modem": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-3.0.8.tgz", - "integrity": "sha512-f0ReSURdM3pcKPNS30mxOHSbaFLcknGmQjwSfmbcdOw1XWKXVhukM3NJHhr7NpY9BIyyWQb0EBo3KQvvuU5egQ==", - "dev": true, - "dependencies": { - "debug": "^4.1.1", - "readable-stream": "^3.5.0", - "split-ca": "^1.0.1", - "ssh2": "^1.11.0" - }, - "engines": { - "node": ">= 8.0" - } - }, - "node_modules/dockerode": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/dockerode/-/dockerode-3.3.5.tgz", - "integrity": "sha512-/0YNa3ZDNeLr/tSckmD69+Gq+qVNhvKfAHNeZJBnp7EOP6RGKV8ORrJHkUn20So5wU+xxT7+1n5u8PjHbfjbSA==", - "dev": true, - "dependencies": { - "@balena/dockerignore": "^1.0.2", - "docker-modem": "^3.0.0", - "tar-fs": "~2.0.1" - }, - "engines": { - "node": ">= 8.0" - } - }, - "node_modules/dockerode/node_modules/tar-fs": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.0.1.tgz", - "integrity": "sha512-6tzWDMeroL87uF/+lin46k+Q+46rAJ0SyPGz7OW7wTgblI273hsBqk2C1j0/xNadNLKDTUL9BukSjB7cwgmlPA==", - "dev": true, - "dependencies": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.0.0" - } - }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/dot-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", - "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/dotenv": { - "version": "16.4.5", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", - "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, - "node_modules/duplexify": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.2.tgz", - "integrity": "sha512-fz3OjcNCHmRP12MJoZMPglx8m4rrFP8rovnk4vT8Fs+aonZoCwGg10dSsQsfP/E62eZcPTMSMP6686fu9Qlqtw==", - "dependencies": { - "end-of-stream": "^1.4.1", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1", - "stream-shift": "^1.0.0" - } - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" - }, - "node_modules/ecdsa-sig-formatter": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", - "dependencies": { - "safe-buffer": "^5.0.1" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.4.681", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.681.tgz", - "integrity": "sha512-1PpuqJUFWoXZ1E54m8bsLPVYwIVCRzvaL+n5cjigGga4z854abDnFRc+cTa2th4S79kyGqya/1xoR7h+Y5G5lg==" - }, - "node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" - }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/enhanced-resolve": { - "version": "5.15.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", - "integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==", - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "dev": true, - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/es-abstract": { - "version": "1.22.4", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.4.tgz", - "integrity": "sha512-vZYJlk2u6qHYxBOTjAeg7qUxHdNfih64Uu2J8QqWgXZ2cri0ZpJAkzDUK/q593+mvKwlxyaxr6F1Q+3LKoQRgg==", - "dev": true, - "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "arraybuffer.prototype.slice": "^1.0.3", - "available-typed-arrays": "^1.0.6", - "call-bind": "^1.0.7", - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "es-set-tostringtag": "^2.0.2", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.6", - "get-intrinsic": "^1.2.4", - "get-symbol-description": "^1.0.2", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.1", - "internal-slot": "^1.0.7", - "is-array-buffer": "^3.0.4", - "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.2", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.13", - "is-weakref": "^1.0.2", - "object-inspect": "^1.13.1", - "object-keys": "^1.1.1", - "object.assign": "^4.1.5", - "regexp.prototype.flags": "^1.5.2", - "safe-array-concat": "^1.1.0", - "safe-regex-test": "^1.0.3", - "string.prototype.trim": "^1.2.8", - "string.prototype.trimend": "^1.0.7", - "string.prototype.trimstart": "^1.0.7", - "typed-array-buffer": "^1.0.1", - "typed-array-byte-length": "^1.0.0", - "typed-array-byte-offset": "^1.0.0", - "typed-array-length": "^1.0.4", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.14" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es-array-method-boxes-properly": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", - "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==", - "dev": true - }, - "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", - "dependencies": { - "get-intrinsic": "^1.2.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-module-lexer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.4.1.tgz", - "integrity": "sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w==" - }, - "node_modules/es-set-tostringtag": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", - "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.2.4", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-shim-unscopables": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", - "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", - "dev": true, - "dependencies": { - "hasown": "^2.0.0" - } - }, - "node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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/escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", - "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.0", - "@humanwhocodes/config-array": "^0.11.14", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-compat-utils": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.1.2.tgz", - "integrity": "sha512-Jia4JDldWnFNIru1Ehx1H5s9/yxiRHY/TimCuUc0jNexew3cF1gI6CYZil1ociakfWO3rRqFjl1mskBblB3RYg==", - "dev": true, - "peer": true, - "engines": { - "node": ">=12" - }, - "peerDependencies": { - "eslint": ">=6.0.0" - } - }, - "node_modules/eslint-config-prettier": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", - "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", - "dev": true, - "bin": { - "eslint-config-prettier": "bin/cli.js" - }, - "peerDependencies": { - "eslint": ">=7.0.0" - } - }, - "node_modules/eslint-config-standard": { - "version": "17.1.0", - "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-17.1.0.tgz", - "integrity": "sha512-IwHwmaBNtDK4zDHQukFDW5u/aTb8+meQWZvNFWkiGmbWjD6bqyuSSBxxXKkCftCUzc1zwCH2m/baCNDLGmuO5Q==", - "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" - } - ], - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "eslint": "^8.0.1", - "eslint-plugin-import": "^2.25.2", - "eslint-plugin-n": "^15.0.0 || ^16.0.0 ", - "eslint-plugin-promise": "^6.0.0" - } - }, - "node_modules/eslint-import-resolver-node": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", - "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", - "dev": true, - "dependencies": { - "debug": "^3.2.7", - "is-core-module": "^2.13.0", - "resolve": "^1.22.4" - } - }, - "node_modules/eslint-import-resolver-node/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-module-utils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz", - "integrity": "sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==", - "dev": true, - "dependencies": { - "debug": "^3.2.7" - }, - "engines": { - "node": ">=4" - }, - "peerDependenciesMeta": { - "eslint": { - "optional": true - } - } - }, - "node_modules/eslint-module-utils/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-plugin-es": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz", - "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==", - "dev": true, - "dependencies": { - "eslint-utils": "^2.0.0", - "regexpp": "^3.0.0" - }, - "engines": { - "node": ">=8.10.0" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - }, - "peerDependencies": { - "eslint": ">=4.19.1" - } - }, - "node_modules/eslint-plugin-es-x": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-es-x/-/eslint-plugin-es-x-7.5.0.tgz", - "integrity": "sha512-ODswlDSO0HJDzXU0XvgZ3lF3lS3XAZEossh15Q2UHjwrJggWeBoKqqEsLTZLXl+dh5eOAozG0zRcYtuE35oTuQ==", - "dev": true, - "peer": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.1.2", - "@eslint-community/regexpp": "^4.6.0", - "eslint-compat-utils": "^0.1.2" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ota-meshi" - }, - "peerDependencies": { - "eslint": ">=8" - } - }, - "node_modules/eslint-plugin-import": { - "version": "2.29.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz", - "integrity": "sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==", - "dev": true, - "dependencies": { - "array-includes": "^3.1.7", - "array.prototype.findlastindex": "^1.2.3", - "array.prototype.flat": "^1.3.2", - "array.prototype.flatmap": "^1.3.2", - "debug": "^3.2.7", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.9", - "eslint-module-utils": "^2.8.0", - "hasown": "^2.0.0", - "is-core-module": "^2.13.1", - "is-glob": "^4.0.3", - "minimatch": "^3.1.2", - "object.fromentries": "^2.0.7", - "object.groupby": "^1.0.1", - "object.values": "^1.1.7", - "semver": "^6.3.1", - "tsconfig-paths": "^3.15.0" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" - } - }, - "node_modules/eslint-plugin-import-esm": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import-esm/-/eslint-plugin-import-esm-2.0.0.tgz", - "integrity": "sha512-LjV8eVtITo1tAld6mBp7HuciCMxzshVCdygu2rGIdyK1mdz1ajLFtMUWX69+Ulob0bYYEwX51U6RQmPVFNTAPg==", - "dev": true, - "engines": { - "node": ">= 18.0.0" - }, - "peerDependencies": { - "eslint": ">=7" - } - }, - "node_modules/eslint-plugin-import/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/eslint-plugin-import/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-plugin-import/node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eslint-plugin-import/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/eslint-plugin-import/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/eslint-plugin-json": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-json/-/eslint-plugin-json-3.1.0.tgz", - "integrity": "sha512-MrlG2ynFEHe7wDGwbUuFPsaT2b1uhuEFhJ+W1f1u+1C2EkXmTYJp4B1aAdQQ8M+CC3t//N/oRKiIVw14L2HR1g==", - "dev": true, - "dependencies": { - "lodash": "^4.17.21", - "vscode-json-languageservice": "^4.1.6" - }, - "engines": { - "node": ">=12.0" - } - }, - "node_modules/eslint-plugin-n": { - "version": "16.6.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-16.6.2.tgz", - "integrity": "sha512-6TyDmZ1HXoFQXnhCTUjVFULReoBPOAjpuiKELMkeP40yffI/1ZRO+d9ug/VC6fqISo2WkuIBk3cvuRPALaWlOQ==", - "dev": true, - "peer": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "builtins": "^5.0.1", - "eslint-plugin-es-x": "^7.5.0", - "get-tsconfig": "^4.7.0", - "globals": "^13.24.0", - "ignore": "^5.2.4", - "is-builtin-module": "^3.2.1", - "is-core-module": "^2.12.1", - "minimatch": "^3.1.2", - "resolve": "^1.22.2", - "semver": "^7.5.3" - }, - "engines": { - "node": ">=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - }, - "peerDependencies": { - "eslint": ">=7.0.0" - } - }, - "node_modules/eslint-plugin-n/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, - "peer": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/eslint-plugin-n/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, - "peer": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/eslint-plugin-node": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", - "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==", - "dev": true, - "dependencies": { - "eslint-plugin-es": "^3.0.0", - "eslint-utils": "^2.0.0", - "ignore": "^5.1.1", - "minimatch": "^3.0.4", - "resolve": "^1.10.1", - "semver": "^6.1.0" - }, - "engines": { - "node": ">=8.10.0" - }, - "peerDependencies": { - "eslint": ">=5.16.0" - } - }, - "node_modules/eslint-plugin-node/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/eslint-plugin-node/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/eslint-plugin-node/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/eslint-plugin-prettier": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.1.3.tgz", - "integrity": "sha512-C9GCVAs4Eq7ZC/XFQHITLiHJxQngdtraXaM+LoUFoFp/lHNl2Zn8f3WQbe9HvTBBQ9YnKFB0/2Ajdqwo5D1EAw==", - "dev": true, - "dependencies": { - "prettier-linter-helpers": "^1.0.0", - "synckit": "^0.8.6" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint-plugin-prettier" - }, - "peerDependencies": { - "@types/eslint": ">=8.0.0", - "eslint": ">=8.0.0", - "eslint-config-prettier": "*", - "prettier": ">=3.0.0" - }, - "peerDependenciesMeta": { - "@types/eslint": { - "optional": true - }, - "eslint-config-prettier": { - "optional": true - } - } - }, - "node_modules/eslint-plugin-promise": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.1.1.tgz", - "integrity": "sha512-tjqWDwVZQo7UIPMeDReOpUgHCmCiH+ePnVT+5zVapL0uuHnegBUs2smM13CzOs2Xb5+MHMRFTs9v24yjba4Oig==", - "dev": true, - "peer": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - } - }, - "node_modules/eslint-plugin-simple-import-sort": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-12.0.0.tgz", - "integrity": "sha512-8o0dVEdAkYap0Cn5kNeklaKcT1nUsa3LITWEuFk3nJifOoD+5JQGoyDUW2W/iPWwBsNBJpyJS9y4je/BgxLcyQ==", - "dev": true, - "peerDependencies": { - "eslint": ">=5.0.0" - } - }, - "node_modules/eslint-plugin-vitest": { - "version": "0.3.22", - "resolved": "https://registry.npmjs.org/eslint-plugin-vitest/-/eslint-plugin-vitest-0.3.22.tgz", - "integrity": "sha512-atkFGQ7aVgcuSeSMDqnyevIyUpfBPMnosksgEPrKE7Y8xQlqG/5z2IQ6UDau05zXaaFv7Iz8uzqvIuKshjZ0Zw==", - "dev": true, - "dependencies": { - "@typescript-eslint/utils": "^6.21.0" - }, - "engines": { - "node": "^18.0.0 || >= 20.0.0" - }, - "peerDependencies": { - "eslint": ">=8.0.0", - "vitest": "*" - }, - "peerDependenciesMeta": { - "@typescript-eslint/eslint-plugin": { - "optional": true - }, - "vitest": { - "optional": true - } - } - }, - "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/scope-manager": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", - "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", - "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", - "dev": true, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/typescript-estree": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", - "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "minimatch": "9.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/utils": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", - "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.12", - "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/typescript-estree": "6.21.0", - "semver": "^7.5.4" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - } - }, - "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/visitor-keys": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", - "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.21.0", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^1.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, - "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/eslint/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", - "dev": true, - "dependencies": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "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/esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", - "dev": true, - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "dev": true, - "dependencies": { - "@types/estree": "^1.0.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "engines": { - "node": ">=0.8.x" - } - }, - "node_modules/execa": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", - "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^8.0.1", - "human-signals": "^5.0.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^4.1.0", - "strip-final-newline": "^3.0.0" - }, - "engines": { - "node": ">=16.17" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/expand-tilde": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", - "integrity": "sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==", - "dependencies": { - "homedir-polyfill": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" - }, - "node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "dev": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "dependencies": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/external-editor/node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dependencies": { - "os-tmpdir": "~1.0.2" - }, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/fast-content-type-parse": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-1.1.0.tgz", - "integrity": "sha512-fBHHqSTFLVnR61C+gltJuE5GkVQMV0S2nqUO8TJ+5Z3qAKG8vAx4FKai1s5jq/inV1+sREynIWSuQ6HgoSXpDQ==" - }, - "node_modules/fast-copy": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/fast-copy/-/fast-copy-3.0.1.tgz", - "integrity": "sha512-Knr7NOtK3HWRYGtHoJrjkaWepqT8thIVGAwt0p0aUs1zqkAzXZV4vo9fFNwyb5fcqK1GKYFYxldQdIDVKhUAfA==" - }, - "node_modules/fast-decode-uri-component": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz", - "integrity": "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==" - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "node_modules/fast-diff": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", - "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", - "dev": true - }, - "node_modules/fast-fifo": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", - "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", - "dev": true - }, - "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==", - "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-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "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==" - }, - "node_modules/fast-json-stringify": { - "version": "5.12.0", - "resolved": "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-5.12.0.tgz", - "integrity": "sha512-7Nnm9UPa7SfHRbHVA1kJQrGXCRzB7LMlAAqHXQFkEQqueJm1V8owm0FsE/2Do55/4CcdhwiLQERaKomOnKQkyA==", - "dependencies": { - "@fastify/merge-json-schemas": "^0.1.0", - "ajv": "^8.10.0", - "ajv-formats": "^2.1.1", - "fast-deep-equal": "^3.1.3", - "fast-uri": "^2.1.0", - "json-schema-ref-resolver": "^1.0.1", - "rfdc": "^1.2.0" - } - }, - "node_modules/fast-json-stringify/node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/fast-json-stringify/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, - "node_modules/fast-querystring": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/fast-querystring/-/fast-querystring-1.1.2.tgz", - "integrity": "sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==", - "dependencies": { - "fast-decode-uri-component": "^1.0.1" - } - }, - "node_modules/fast-redact": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.3.0.tgz", - "integrity": "sha512-6T5V1QK1u4oF+ATxs1lWUmlEk6P2T9HqJG3e2DnHOdVgZy2rFJBoEnrIedcTXlkAHU/zKC+7KETJ+KGGKwxgMQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/fast-safe-stringify": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", - "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" - }, - "node_modules/fast-unique-numbers": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/fast-unique-numbers/-/fast-unique-numbers-9.0.0.tgz", - "integrity": "sha512-lgIjiflW23W7qgagregmo5FFzM+m4/dWaDUVneRi2AV7o2k5npggeEX7srSKlYfJU9fKXvQV2Gzk3272fJT65w==", - "dependencies": { - "@babel/runtime": "^7.23.9", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.2.0" - } - }, - "node_modules/fast-uri": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-2.3.0.tgz", - "integrity": "sha512-eel5UKGn369gGEWOqBShmFJWfq/xSJvsgDzgLYC845GneayWvXBf0lJCBn5qTABfewy1ZDPoaR5OZCP+kssfuw==" - }, - "node_modules/fast-xml-parser": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.2.5.tgz", - "integrity": "sha512-B9/wizE4WngqQftFPmdaMYlXoJlJOYxGQOanC77fq9k8+Z0v5dDSVh+3glErdIROP//s/jgb7ZuxKfB8nVyo0g==", - "funding": [ - { - "type": "paypal", - "url": "https://paypal.me/naturalintelligence" - }, - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "dependencies": { - "strnum": "^1.0.5" - }, - "bin": { - "fxparser": "src/cli/cli.js" - } - }, - "node_modules/fastify": { - "version": "4.26.1", - "resolved": "https://registry.npmjs.org/fastify/-/fastify-4.26.1.tgz", - "integrity": "sha512-tznA/G55dsxzM5XChBfcvVSloG2ejeeotfPPJSFaWmHyCDVGMpvf3nRNbsCb/JTBF9RmQFBfuujWt3Nphjesng==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "dependencies": { - "@fastify/ajv-compiler": "^3.5.0", - "@fastify/error": "^3.4.0", - "@fastify/fast-json-stringify-compiler": "^4.3.0", - "abstract-logging": "^2.0.1", - "avvio": "^8.3.0", - "fast-content-type-parse": "^1.1.0", - "fast-json-stringify": "^5.8.0", - "find-my-way": "^8.0.0", - "light-my-request": "^5.11.0", - "pino": "^8.17.0", - "process-warning": "^3.0.0", - "proxy-addr": "^2.0.7", - "rfdc": "^1.3.0", - "secure-json-parse": "^2.7.0", - "semver": "^7.5.4", - "toad-cache": "^3.3.0" - } - }, - "node_modules/fastify-plugin": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/fastify-plugin/-/fastify-plugin-4.5.1.tgz", - "integrity": "sha512-stRHYGeuqpEZTL1Ef0Ovr2ltazUT9g844X5z/zEBFLG8RYlpDiOCIG+ATvYEp+/zmc7sN29mcIMp8gvYplYPIQ==" - }, - "node_modules/fastq": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", - "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/feed": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/feed/-/feed-4.2.2.tgz", - "integrity": "sha512-u5/sxGfiMfZNtJ3OvQpXcvotFpYkL0n9u9mM2vkui2nGo8b4wvDkJ8gAkYqbA8QpGyFCv3RK0Z+Iv+9veCS9bQ==", - "dev": true, - "dependencies": { - "xml-js": "^1.6.11" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "dependencies": { - "escape-string-regexp": "^1.0.5" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/figures/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==", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "dependencies": { - "flat-cache": "^3.0.4" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "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==", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-my-way": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-8.1.0.tgz", - "integrity": "sha512-41QwjCGcVTODUmLLqTMeoHeiozbMXYMAE1CKFiDyi9zVZ2Vjh0yz3MF0WQZoIb+cmzP/XlbFjlF2NtJmvZHznA==", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-querystring": "^1.0.0", - "safe-regex2": "^2.0.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/findup-sync": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-5.0.0.tgz", - "integrity": "sha512-MzwXju70AuyflbgeOhzvQWAvvQdo1XL0A9bVvlXsYcFEBM87WR4OakL4OfZq+QRmr+duJubio+UtNQCPsVESzQ==", - "dependencies": { - "detect-file": "^1.0.0", - "is-glob": "^4.0.3", - "micromatch": "^4.0.4", - "resolve-dir": "^1.0.1" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/fined": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fined/-/fined-2.0.0.tgz", - "integrity": "sha512-OFRzsL6ZMHz5s0JrsEr+TpdGNCtrVtnuG3x1yzGNiQHT0yaDnXAj8V/lWcpJVrnoDpcwXcASxAZYbuXda2Y82A==", - "dependencies": { - "expand-tilde": "^2.0.2", - "is-plain-object": "^5.0.0", - "object.defaults": "^1.1.0", - "object.pick": "^1.3.0", - "parse-filepath": "^1.0.2" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/flagged-respawn": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-2.0.0.tgz", - "integrity": "sha512-Gq/a6YCi8zexmGHMuJwahTGzXlAZAOsbCVKduWXC6TlLCjjFRlExMJc4GC2NYPYZ0r/brw9P7CpRgQmlPVeOoA==", - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/flat-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", - "dev": true, - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/flat-cache/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/flat-cache/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/flat-cache/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/flat-cache/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/flatted": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", - "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", - "dev": true - }, - "node_modules/focus-trap": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.5.4.tgz", - "integrity": "sha512-N7kHdlgsO/v+iD/dMoJKtsSqs5Dz/dXZVebRgJw23LDk+jMi/974zyiOYDziY2JPp8xivq9BmUGwIJMiuSBi7w==", - "dev": true, - "dependencies": { - "tabbable": "^6.2.0" - } - }, - "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "dependencies": { - "is-callable": "^1.1.3" - } - }, - "node_modules/for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/for-own": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", - "integrity": "sha512-0OABksIGrxKK8K4kynWkQ7y1zounQxP+CWnyclVwj81KW3vlLlGUx57DKGcP/LH216GzqnstnPocF16Nxs0Ycg==", - "dependencies": { - "for-in": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/foreground-child": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", - "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", - "dependencies": { - "cross-spawn": "^7.0.0", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/form-data": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", - "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 0.12" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/from2": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", - "integrity": "sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==", - "dependencies": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.0" - } - }, - "node_modules/from2/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" - }, - "node_modules/from2/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/from2/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "node_modules/from2/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "dev": true - }, - "node_modules/fs-monkey": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.5.tgz", - "integrity": "sha512-8uMbBjrhzW76TYgEV27Y5E//W2f/lTFmx78P2w19FZSxarhI/798APGQyuGCwmkNxgwGRhrLfvWyLBvNtuOmew==" - }, - "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==" - }, - "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.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/function.prototype.name": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", - "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "functions-have-names": "^1.2.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gaxios": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.3.0.tgz", - "integrity": "sha512-p+ggrQw3fBwH2F5N/PAI4k/G/y1art5OxKpb2J2chwNNHM4hHuAOtivjPuirMF4KNKwTTUal/lPfL2+7h2mEcg==", - "dependencies": { - "extend": "^3.0.2", - "https-proxy-agent": "^7.0.1", - "is-stream": "^2.0.0", - "node-fetch": "^2.6.9" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/gaxios/node_modules/agent-base": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", - "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", - "dependencies": { - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/gaxios/node_modules/https-proxy-agent": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz", - "integrity": "sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==", - "dependencies": { - "agent-base": "^7.0.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/gaxios/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==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/gcp-metadata": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.0.tgz", - "integrity": "sha512-Jh/AIwwgaxan+7ZUUmRLCjtchyDiqh4KjBJ5tW3plBZb5iL/BPcso8A5DlzeD9qlw0duCamnNdpFjxwaT0KyKg==", - "dependencies": { - "gaxios": "^6.0.0", - "json-bigint": "^1.0.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/generic-pool": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz", - "integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==", - "engines": { - "node": ">= 4" - } - }, - "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==", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-east-asian-width": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.2.0.tgz", - "integrity": "sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/get-func-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", - "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-port": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", - "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/get-stream": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", - "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", - "dev": true, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/get-symbol-description": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", - "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.5", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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/git-cliff": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/git-cliff/-/git-cliff-2.0.4.tgz", - "integrity": "sha512-w1wmL9cxaBZq8LjxX1w6NLs6ENqX4Eoa8DNMhReLkKPnP9+SmfsL0oyJxUS14jA+oDLs/imnWnVbYWPmyOQPtw==", - "dev": true, - "bin": { - "git-cliff": "lib/index.js" - }, - "optionalDependencies": { - "git-cliff-darwin-arm64": "2.0.4", - "git-cliff-darwin-x64": "2.0.4", - "git-cliff-linux-arm64": "2.0.4", - "git-cliff-linux-x64": "2.0.4", - "git-cliff-windows-arm64": "2.0.4", - "git-cliff-windows-x64": "2.0.4" - } - }, - "node_modules/git-cliff-darwin-arm64": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/git-cliff-darwin-arm64/-/git-cliff-darwin-arm64-2.0.4.tgz", - "integrity": "sha512-+1olIX5nepDFy3Ahpe5nwqFIowIZtDZwsUmpDHZX7fQDgEeisjisYp6ksUWN0klhvFdoSPbBvdHj5m9HBzm+9Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/git-cliff-darwin-x64": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/git-cliff-darwin-x64/-/git-cliff-darwin-x64-2.0.4.tgz", - "integrity": "sha512-0V8Db8BiRkEW90MAtV5qNsM4hN3vapFuXEr77Epxj1YPSh0aMgQ9jveueOrwNEYt+t8lupkORrsKUPjHE1440g==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/git-cliff-linux-arm64": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/git-cliff-linux-arm64/-/git-cliff-linux-arm64-2.0.4.tgz", - "integrity": "sha512-EF34tm10a7CiKBI73T1JHEEGEiBpaoapNnYL5WuhA7Uz/Vl7nUHmIJnmdjrmmT5TSTMXYm31CGUuhHrFa3KGNw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/git-cliff-linux-x64": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/git-cliff-linux-x64/-/git-cliff-linux-x64-2.0.4.tgz", - "integrity": "sha512-YdAY7CzCyyjYEffbEK9eBeJo84+IVM5893enXAI6XVpMsM3l9Y0lucfiX9aD1J6xasYsbpxH9ziziWE13q0rvg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/git-cliff-windows-arm64": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/git-cliff-windows-arm64/-/git-cliff-windows-arm64-2.0.4.tgz", - "integrity": "sha512-r/aeP4HaT41lKVHmV+BXM1gLLRtpXeruTmtNw5Y/lmFZ/QSwBXrU/ZhYIG8yuQkwisQo9wNwWrknU5ZMUm94BQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/git-cliff-windows-x64": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/git-cliff-windows-x64/-/git-cliff-windows-x64-2.0.4.tgz", - "integrity": "sha512-2LWV0GrfU4ZFNVZCTjP9Le7tGeFy2rRf9mhcpDhzUCBEKxbn1xjgS11qcyNr4ZIws/RoTwe8K/KNbzF0XlRQWQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/glob": { - "version": "10.3.10", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", - "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^2.3.5", - "minimatch": "^9.0.1", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", - "path-scurry": "^1.10.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==" - }, - "node_modules/global-modules": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", - "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", - "dependencies": { - "global-prefix": "^1.0.1", - "is-windows": "^1.0.1", - "resolve-dir": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/global-prefix": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", - "integrity": "sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==", - "dependencies": { - "expand-tilde": "^2.0.2", - "homedir-polyfill": "^1.0.1", - "ini": "^1.3.4", - "is-windows": "^1.0.1", - "which": "^1.2.14" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/global-prefix/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, - "node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globalthis": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", - "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", - "dev": true, - "dependencies": { - "define-properties": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/google-auth-library": { - "version": "9.6.3", - "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.6.3.tgz", - "integrity": "sha512-4CacM29MLC2eT9Cey5GDVK4Q8t+MMp8+OEdOaqD9MG6b0dOyLORaaeJMPQ7EESVgm/+z5EKYyFLxgzBJlJgyHQ==", - "dependencies": { - "base64-js": "^1.3.0", - "ecdsa-sig-formatter": "^1.0.11", - "gaxios": "^6.1.1", - "gcp-metadata": "^6.1.0", - "gtoken": "^7.0.0", - "jws": "^4.0.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/google-gax": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-4.3.1.tgz", - "integrity": "sha512-qpSfslpwqToIgQ+Tf3MjWIDjYK4UFIZ0uz6nLtttlW9N1NQA4PhGf9tlGo6KDYJ4rgL2w4CjXVd0z5yeNpN/Iw==", - "dependencies": { - "@grpc/grpc-js": "~1.10.0", - "@grpc/proto-loader": "^0.7.0", - "@types/long": "^4.0.0", - "abort-controller": "^3.0.0", - "duplexify": "^4.0.0", - "google-auth-library": "^9.3.0", - "node-fetch": "^2.6.1", - "object-hash": "^3.0.0", - "proto3-json-serializer": "^2.0.0", - "protobufjs": "7.2.6", - "retry-request": "^7.0.0", - "uuid": "^9.0.1" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/google-gax/node_modules/@grpc/grpc-js": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.10.1.tgz", - "integrity": "sha512-55ONqFytZExfOIjF1RjXPcVmT/jJqFzbbDqxK9jmRV4nxiYWtL9hENSW1Jfx0SdZfrvoqd44YJ/GJTqfRrawSQ==", - "dependencies": { - "@grpc/proto-loader": "^0.7.8", - "@types/node": ">=12.12.47" - }, - "engines": { - "node": "^8.13.0 || >=10.10.0" - } - }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dependencies": { - "get-intrinsic": "^1.1.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/got": { - "version": "11.8.6", - "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", - "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", - "dependencies": { - "@sindresorhus/is": "^4.0.0", - "@szmarczak/http-timer": "^4.0.5", - "@types/cacheable-request": "^6.0.1", - "@types/responselike": "^1.0.0", - "cacheable-lookup": "^5.0.3", - "cacheable-request": "^7.0.2", - "decompress-response": "^6.0.0", - "http2-wrapper": "^1.0.0-beta.5.2", - "lowercase-keys": "^2.0.0", - "p-cancelable": "^2.0.0", - "responselike": "^2.0.0" - }, - "engines": { - "node": ">=10.19.0" - }, - "funding": { - "url": "https://github.com/sindresorhus/got?sponsor=1" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" - }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true - }, - "node_modules/gray-matter": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz", - "integrity": "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==", - "dev": true, - "dependencies": { - "js-yaml": "^3.13.1", - "kind-of": "^6.0.2", - "section-matter": "^1.0.0", - "strip-bom-string": "^1.0.0" - }, - "engines": { - "node": ">=6.0" - } - }, - "node_modules/gray-matter/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/gray-matter/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/gtoken": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz", - "integrity": "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==", - "dependencies": { - "gaxios": "^6.0.0", - "jws": "^4.0.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/handlebars": { - "version": "4.7.8", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", - "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", - "dependencies": { - "minimist": "^1.2.5", - "neo-async": "^2.6.2", - "source-map": "^0.6.1", - "wordwrap": "^1.0.0" - }, - "bin": { - "handlebars": "bin/handlebars" - }, - "engines": { - "node": ">=0.4.7" - }, - "optionalDependencies": { - "uglify-js": "^3.1.4" - } - }, - "node_modules/has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", - "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz", - "integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/header-case": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/header-case/-/header-case-2.0.4.tgz", - "integrity": "sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q==", - "dependencies": { - "capital-case": "^1.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/heap-js": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/heap-js/-/heap-js-2.3.0.tgz", - "integrity": "sha512-E5303mzwQ+4j/n2J0rDvEPBN7GKjhis10oHiYOgjxsmxYgqG++hz9NyLLOXttzH8as/DyiBHYpUrJTZWYaMo8Q==", - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/helmet": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/helmet/-/helmet-7.1.0.tgz", - "integrity": "sha512-g+HZqgfbpXdCkme/Cd/mZkV0aV3BZZZSugecH03kl38m/Kmdx8jKjBikpDj2cr+Iynv4KpYEviojNdTJActJAg==", - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/help-me": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz", - "integrity": "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==" - }, - "node_modules/homedir-polyfill": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", - "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", - "dependencies": { - "parse-passwd": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/hono": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/hono/-/hono-4.0.6.tgz", - "integrity": "sha512-yL8Dp4mNscQj3zqMsmuHRoDoJlbP/3Jjz5HrIcd8a7MEWDr/O8oPgEQx2saVG+NX8mgEeEDQOuG2cgXCc9VuoA==", - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/hookable": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz", - "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", - "dev": true - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "node_modules/http-cache-semantics": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", - "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==" - }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/http-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", - "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", - "dependencies": { - "@tootallnate/once": "2", - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/http2-wrapper": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", - "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", - "dependencies": { - "quick-lru": "^5.1.1", - "resolve-alpn": "^1.0.0" - }, - "engines": { - "node": ">=10.19.0" - } - }, - "node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/human-signals": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", - "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", - "dev": true, - "engines": { - "node": ">=16.17.0" - } - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "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/ignore": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", - "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-in-the-middle": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.7.1.tgz", - "integrity": "sha512-1LrZPDtW+atAxH42S6288qyDFNQ2YCty+2mxEPRtfazH6Z5QwkaBSTS2ods7hnVJioF6rkRfNoA6A/MstpFXLg==", - "dependencies": { - "acorn": "^8.8.2", - "acorn-import-assertions": "^1.9.0", - "cjs-module-lexer": "^1.2.2", - "module-details-from-path": "^1.0.3" - } - }, - "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/indent-string": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", - "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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==", - "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==" - }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" - }, - "node_modules/inquirer": { - "version": "9.2.15", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-9.2.15.tgz", - "integrity": "sha512-vI2w4zl/mDluHt9YEQ/543VTCwPKWiHzKtm9dM2V0NdFcqEexDAjUHzO1oA60HRNaVifGXXM1tRRNluLVHa0Kg==", - "dependencies": { - "@ljharb/through": "^2.3.12", - "ansi-escapes": "^4.3.2", - "chalk": "^5.3.0", - "cli-cursor": "^3.1.0", - "cli-width": "^4.1.0", - "external-editor": "^3.1.0", - "figures": "^3.2.0", - "lodash": "^4.17.21", - "mute-stream": "1.0.0", - "ora": "^5.4.1", - "run-async": "^3.0.0", - "rxjs": "^7.8.1", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^6.2.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/inquirer/node_modules/chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/inquirer/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==" - }, - "node_modules/inquirer/node_modules/is-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", - "engines": { - "node": ">=8" - } - }, - "node_modules/inquirer/node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/inquirer/node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/inquirer/node_modules/log-symbols/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "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/inquirer/node_modules/ora": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", - "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", - "dependencies": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/inquirer/node_modules/ora/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "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/inquirer/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==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/inquirer/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/internal-slot": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", - "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", - "dev": true, - "dependencies": { - "es-errors": "^1.3.0", - "hasown": "^2.0.0", - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/interpret": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", - "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==", - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/into-stream": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-6.0.0.tgz", - "integrity": "sha512-XHbaOAvP+uFKUFsOgoNPRjLkwB+I22JFPFe5OjTkQ0nwgj6+pSjb4NmB6VMxaPshLiOf+zcpOCBQuLwC1KHhZA==", - "dependencies": { - "from2": "^2.3.0", - "p-is-promise": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-absolute": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", - "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", - "dependencies": { - "is-relative": "^1.0.0", - "is-windows": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-arguments": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", - "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-array-buffer": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", - "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dev": true, - "dependencies": { - "has-bigints": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-builtin-module": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", - "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", - "dev": true, - "peer": true, - "dependencies": { - "builtin-modules": "^3.3.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-core-module": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", - "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", - "dependencies": { - "hasown": "^2.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-generator-function": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", - "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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==", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-interactive": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", - "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-negative-zero": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", - "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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==", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-path-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-3.0.0.tgz", - "integrity": "sha512-kyiNFFLU0Ampr6SDZitD/DwUo4Zs1nSdnygUBqsu3LooL00Qvb5j+UnvApUn/TTj1J3OuE6BTdQ5rudKmU2ZaA==", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-plain-object": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-relative": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", - "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", - "dependencies": { - "is-unc-path": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", - "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", - "dev": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "dev": true, - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-typed-array": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", - "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", - "dependencies": { - "which-typed-array": "^1.1.14" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-unc-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", - "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", - "dependencies": { - "unc-path-regex": "^0.1.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-unicode-supported": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.0.0.tgz", - "integrity": "sha512-FRdAyx5lusK1iHG0TWpVtk9+1i+GjrzRffhDg4ovQ7mcidMQ6mj+MhKPmvh7Xwyv5gIS06ns49CA7Sqg7lC22Q==", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dependencies": { - "is-docker": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true - }, - "node_modules/isbinaryfile": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-5.0.2.tgz", - "integrity": "sha512-GvcjojwonMjWbTkfMpnVHVqXW/wKMYDfEpY94/8zy8HFMOqb/VL6oeONq9v87q4ttVlaTLnGXnJD4B5B1OTGIg==", - "engines": { - "node": ">= 18.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/gjtorikian/" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" - }, - "node_modules/isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", - "dev": true, - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-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.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", - "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", - "dev": true, - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jackspeak": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", - "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", - "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": ">= 10.13.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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/joi": { - "version": "17.12.2", - "resolved": "https://registry.npmjs.org/joi/-/joi-17.12.2.tgz", - "integrity": "sha512-RonXAIzCiHLc8ss3Ibuz45u28GOsWE1UpfDXLbN/9NKbL4tCJf8TWYVKsoYuuh+sAUt7fsSNpA+r2+TBA6Wjmw==", - "dependencies": { - "@hapi/hoek": "^9.3.0", - "@hapi/topo": "^5.1.0", - "@sideway/address": "^4.1.5", - "@sideway/formula": "^3.0.1", - "@sideway/pinpoint": "^2.0.0" - } - }, - "node_modules/joycon": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", - "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", - "engines": { - "node": ">=10" - } - }, - "node_modules/js-sdsl": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz", - "integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/js-sdsl" - } - }, - "node_modules/js-tokens": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-8.0.3.tgz", - "integrity": "sha512-UfJMcSJc+SEXEl9lH/VLHSZbThQyLpw1vLO1Lb+j4RWDvG3N2f7yj3PVQA3cmkTBNldJ9eFnM+xEXxHIXrYiJw==", - "dev": true - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/json-bigint": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", - "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", - "dependencies": { - "bignumber.js": "^9.0.0" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" - }, - "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==" - }, - "node_modules/json-schema-ref-resolver": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-schema-ref-resolver/-/json-schema-ref-resolver-1.0.1.tgz", - "integrity": "sha512-EJAj1pgHc1hxF6vo2Z3s69fMjO1INq6eGHXZ8Z6wCQeldCuwxGK9Sxf4/cScGn3FZubCVUehfWtcDM/PLteCQw==", - "dependencies": { - "fast-deep-equal": "^3.1.3" - } - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true - }, - "node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "dev": true, - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/jsonc-parser": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz", - "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==", - "dev": true - }, - "node_modules/jsonwebtoken": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", - "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", - "dependencies": { - "jws": "^3.2.2", - "lodash.includes": "^4.3.0", - "lodash.isboolean": "^3.0.3", - "lodash.isinteger": "^4.0.4", - "lodash.isnumber": "^3.0.3", - "lodash.isplainobject": "^4.0.6", - "lodash.isstring": "^4.0.1", - "lodash.once": "^4.0.0", - "ms": "^2.1.1", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=12", - "npm": ">=6" - } - }, - "node_modules/jsonwebtoken/node_modules/jwa": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", - "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", - "dependencies": { - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/jsonwebtoken/node_modules/jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", - "dependencies": { - "jwa": "^1.4.1", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/just-extend": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-6.2.0.tgz", - "integrity": "sha512-cYofQu2Xpom82S6qD778jBDpwvvy39s1l/hrYij2u9AMdQcGRpaBu6kY4mVhuno5kJVi1DAz4aiphA2WI1/OAw==", - "dev": true - }, - "node_modules/jwa": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", - "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", - "dependencies": { - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/jws": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", - "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", - "dependencies": { - "jwa": "^2.0.0", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/lazystream": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", - "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", - "dev": true, - "dependencies": { - "readable-stream": "^2.0.5" - }, - "engines": { - "node": ">= 0.6.3" - } - }, - "node_modules/lazystream/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true - }, - "node_modules/lazystream/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/lazystream/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "node_modules/lazystream/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/liftoff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-4.0.0.tgz", - "integrity": "sha512-rMGwYF8q7g2XhG2ulBmmJgWv25qBsqRbDn5gH0+wnuyeFt7QBJlHJmtg5qEdn4pN6WVAUMgXnIxytMFRX9c1aA==", - "dependencies": { - "extend": "^3.0.2", - "findup-sync": "^5.0.0", - "fined": "^2.0.0", - "flagged-respawn": "^2.0.0", - "is-plain-object": "^5.0.0", - "object.map": "^1.0.1", - "rechoir": "^0.8.0", - "resolve": "^1.20.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/light-my-request": { - "version": "5.11.1", - "resolved": "https://registry.npmjs.org/light-my-request/-/light-my-request-5.11.1.tgz", - "integrity": "sha512-KXAh2m6VRlkWCk2KfmHE7tLBXKh30JE0tXUJY4dNxje4oLmPKUqlUfImiEQZLphx+Z9KTQcVv4DjGnJxkVOIbA==", - "dependencies": { - "cookie": "^0.6.0", - "process-warning": "^2.0.0", - "set-cookie-parser": "^2.4.1" - } - }, - "node_modules/light-my-request/node_modules/process-warning": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-2.3.2.tgz", - "integrity": "sha512-n9wh8tvBe5sFmsqlg+XQhaQLumwpqoAUruLwjCopgTmUBjJ/fjtBsJzKleCaIGBOMXYEhp1YfKl4d7rJ5ZKJGA==" - }, - "node_modules/loader-runner": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", - "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", - "engines": { - "node": ">=6.11.5" - } - }, - "node_modules/local-pkg": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.0.tgz", - "integrity": "sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==", - "dev": true, - "dependencies": { - "mlly": "^1.4.2", - "pkg-types": "^1.0.3" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "node_modules/lodash.camelcase": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" - }, - "node_modules/lodash.defaults": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", - "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", - "dev": true - }, - "node_modules/lodash.difference": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", - "integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==", - "dev": true - }, - "node_modules/lodash.flatten": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", - "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==", - "dev": true - }, - "node_modules/lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==" - }, - "node_modules/lodash.includes": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", - "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" - }, - "node_modules/lodash.isboolean": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", - "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" - }, - "node_modules/lodash.isinteger": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", - "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" - }, - "node_modules/lodash.isnumber": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", - "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" - }, - "node_modules/lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" - }, - "node_modules/lodash.isstring": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" - }, - "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/lodash.once": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" - }, - "node_modules/lodash.union": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", - "integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==", - "dev": true - }, - "node_modules/log-symbols": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz", - "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==", - "dependencies": { - "chalk": "^5.3.0", - "is-unicode-supported": "^1.3.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-symbols/node_modules/chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/log-symbols/node_modules/is-unicode-supported": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", - "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/long": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", - "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" - }, - "node_modules/loupe": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", - "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", - "dev": true, - "dependencies": { - "get-func-name": "^2.0.1" - } - }, - "node_modules/lower-case": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", - "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", - "dependencies": { - "tslib": "^2.0.3" - } - }, - "node_modules/lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", - "engines": { - "node": ">=8" - } - }, - "node_modules/lru-cache": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", - "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", - "engines": { - "node": "14 || >=16.14" - } - }, - "node_modules/lunr": { - "version": "2.3.9", - "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", - "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", - "dev": true - }, - "node_modules/magic-string": { - "version": "0.30.7", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.7.tgz", - "integrity": "sha512-8vBuFF/I/+OSLRmdf2wwFCJCz+nSn0m6DPvGH1fS/KiQoSaR+sETbov0eIk9KhEKy8CYqIkIAnbohxT/4H0kuA==", - "dev": true, - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.15" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/magicast": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.3.tgz", - "integrity": "sha512-ZbrP1Qxnpoes8sz47AM0z08U+jW6TyRgZzcWy3Ma3vDhJttwMwAFDMMQFobwdBxByBD46JYmxRzeF7w2+wJEuw==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.23.6", - "@babel/types": "^7.23.6", - "source-map-js": "^1.0.2" - } - }, - "node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-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/make-iterator": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz", - "integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==", - "dependencies": { - "kind-of": "^6.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/mark.js": { - "version": "8.11.1", - "resolved": "https://registry.npmjs.org/mark.js/-/mark.js-8.11.1.tgz", - "integrity": "sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==", - "dev": true - }, - "node_modules/marked": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", - "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", - "dev": true, - "bin": { - "marked": "bin/marked.js" - }, - "engines": { - "node": ">= 12" - } - }, - "node_modules/memfs": { - "version": "4.7.7", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.7.7.tgz", - "integrity": "sha512-x9qc6k88J/VVwnfTkJV8pRRswJ2156Rc4w5rciRqKceFDZ0y1MqsNL9pkg5sE0GOcDzZYbonreALhaHzg1siFw==", - "dependencies": { - "tslib": "^2.0.0" - }, - "engines": { - "node": ">= 4.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/streamich" - } - }, - "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==" - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "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==", - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mime": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", - "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-fn": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", - "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", - "engines": { - "node": ">=4" - } - }, - "node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/minipass": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", - "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/minisearch": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/minisearch/-/minisearch-6.3.0.tgz", - "integrity": "sha512-ihFnidEeU8iXzcVHy74dhkxh/dn8Dc08ERl0xwoMMGqp4+LvRSCgicb+zGqWthVokQKvCSxITlh3P08OzdTYCQ==", - "dev": true - }, - "node_modules/mitt": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", - "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", - "dev": true - }, - "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/mkdirp-classic": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", - "dev": true - }, - "node_modules/mlly": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.6.1.tgz", - "integrity": "sha512-vLgaHvaeunuOXHSmEbZ9izxPx3USsk8KCQ8iC+aTlp5sKRSoZvwhHh5L9VbKSaVC6sJDqbyohIS76E2VmHIPAA==", - "dev": true, - "dependencies": { - "acorn": "^8.11.3", - "pathe": "^1.1.2", - "pkg-types": "^1.0.3", - "ufo": "^1.3.2" - } - }, - "node_modules/mnemonist": { - "version": "0.39.6", - "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.39.6.tgz", - "integrity": "sha512-A/0v5Z59y63US00cRSLiloEIw3t5G+MiKz4BhX21FI+YBJXBOGW0ohFxTxO08dsOYlzxo87T7vGfZKYp2bcAWA==", - "dependencies": { - "obliterator": "^2.0.1" - } - }, - "node_modules/module-details-from-path": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.3.tgz", - "integrity": "sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==" - }, - "node_modules/mqtt": { - "version": "5.3.5", - "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-5.3.5.tgz", - "integrity": "sha512-xd7qt/LEM721U6yHQcqjlaAKXL1Fsqf/MXq6C2WPi/6OXK2jdSzL1eZ7ZUgjea7IY2yFLRWD5LNdT1mL0arPoA==", - "dependencies": { - "@types/readable-stream": "^4.0.5", - "@types/ws": "^8.5.9", - "commist": "^3.2.0", - "concat-stream": "^2.0.0", - "debug": "^4.3.4", - "help-me": "^5.0.0", - "lru-cache": "^10.0.1", - "minimist": "^1.2.8", - "mqtt": "^5.2.0", - "mqtt-packet": "^9.0.0", - "number-allocator": "^1.0.14", - "readable-stream": "^4.4.2", - "reinterval": "^1.1.0", - "rfdc": "^1.3.0", - "split2": "^4.2.0", - "worker-timers": "^7.0.78", - "ws": "^8.14.2" - }, - "bin": { - "mqtt": "build/bin/mqtt.js", - "mqtt_pub": "build/bin/pub.js", - "mqtt_sub": "build/bin/sub.js" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/mqtt-packet": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-9.0.0.tgz", - "integrity": "sha512-8v+HkX+fwbodsWAZIZTI074XIoxVBOmPeggQuDFCGg1SqNcC+uoRMWu7J6QlJPqIUIJXmjNYYHxBBLr1Y/Df4w==", - "dependencies": { - "bl": "^6.0.8", - "debug": "^4.3.4", - "process-nextick-args": "^2.0.1" - } - }, - "node_modules/mqtt-packet/node_modules/bl": { - "version": "6.0.11", - "resolved": "https://registry.npmjs.org/bl/-/bl-6.0.11.tgz", - "integrity": "sha512-Ok/NWrEA0mlEEbWzckkZVLq6Nv1m2xZ+i9Jq5hZ9Ph/YEcP5dExqls9wUzpluhQRPzdeT8oZNOXAytta6YN8pQ==", - "dependencies": { - "@types/readable-stream": "^4.0.0", - "buffer": "^6.0.3", - "inherits": "^2.0.4", - "readable-stream": "^4.2.0" - } - }, - "node_modules/mqtt-packet/node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "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": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "node_modules/mqtt-packet/node_modules/readable-stream": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", - "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", - "dependencies": { - "abort-controller": "^3.0.0", - "buffer": "^6.0.3", - "events": "^3.3.0", - "process": "^0.11.10", - "string_decoder": "^1.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/mqtt/node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "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": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "node_modules/mqtt/node_modules/readable-stream": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", - "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", - "dependencies": { - "abort-controller": "^3.0.0", - "buffer": "^6.0.3", - "events": "^3.3.0", - "process": "^0.11.10", - "string_decoder": "^1.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "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==" - }, - "node_modules/mute-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", - "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/nan": { - "version": "2.18.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.18.0.tgz", - "integrity": "sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w==", - "dev": true, - "optional": true - }, - "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/nats": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/nats/-/nats-2.19.0.tgz", - "integrity": "sha512-TuOAqPljCRpfHPo2o3midezchqYJUOOnK/YLmYf9rdoshzlYN1xvCd9dAKveVB6Bfubp/m63eN3l3ukfn43JOg==", - "dependencies": { - "nkeys.js": "1.0.5" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "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/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" - }, - "node_modules/nise": { - "version": "5.1.9", - "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.9.tgz", - "integrity": "sha512-qOnoujW4SV6e40dYxJOb3uvuoPHtmLzIk4TFo+j0jPJoC+5Z9xja5qH5JZobEPsa8+YYphMrOSwnrshEhG2qww==", - "dev": true, - "dependencies": { - "@sinonjs/commons": "^3.0.0", - "@sinonjs/fake-timers": "^11.2.2", - "@sinonjs/text-encoding": "^0.7.2", - "just-extend": "^6.2.0", - "path-to-regexp": "^6.2.1" - } - }, - "node_modules/nkeys.js": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nkeys.js/-/nkeys.js-1.0.5.tgz", - "integrity": "sha512-u25YnRPHiGVsNzwyHnn+PT90sgAhnS8jUJ1nxmkHMFYCJ6+Ic0lv291w7uhRBpJVJ3PH2GWbYqA151lGCRrB5g==", - "dependencies": { - "tweetnacl": "1.0.3" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/nkeys.js/node_modules/tweetnacl": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", - "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" - }, - "node_modules/no-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", - "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", - "dependencies": { - "lower-case": "^2.0.2", - "tslib": "^2.0.3" - } - }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/node-plop": { - "version": "0.32.0", - "resolved": "https://registry.npmjs.org/node-plop/-/node-plop-0.32.0.tgz", - "integrity": "sha512-lKFSRSRuDHhwDKMUobdsvaWCbbDRbV3jMUSMiajQSQux1aNUevAZVxUHc2JERI//W8ABPRbi3ebYuSuIzkNIpQ==", - "dependencies": { - "@types/inquirer": "^9.0.3", - "change-case": "^4.1.2", - "del": "^7.1.0", - "globby": "^13.2.2", - "handlebars": "^4.7.8", - "inquirer": "^9.2.10", - "isbinaryfile": "^5.0.0", - "lodash.get": "^4.4.2", - "lower-case": "^2.0.2", - "mkdirp": "^3.0.1", - "resolve": "^1.22.4", - "title-case": "^3.0.3", - "upper-case": "^2.0.2" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/node-plop/node_modules/globby": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/globby/-/globby-13.2.2.tgz", - "integrity": "sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==", - "dependencies": { - "dir-glob": "^3.0.1", - "fast-glob": "^3.3.0", - "ignore": "^5.2.4", - "merge2": "^1.4.1", - "slash": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/node-plop/node_modules/mkdirp": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", - "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", - "bin": { - "mkdirp": "dist/cjs/src/bin.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/node-plop/node_modules/slash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", - "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/node-releases": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", - "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==" - }, - "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/normalize-url": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", - "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm-run-path": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", - "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", - "dev": true, - "dependencies": { - "path-key": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm-run-path/node_modules/path-key": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/number-allocator": { - "version": "1.0.14", - "resolved": "https://registry.npmjs.org/number-allocator/-/number-allocator-1.0.14.tgz", - "integrity": "sha512-OrL44UTVAvkKdOdRQZIJpLkAdjXGTRda052sN4sO77bKEzYYqWKMBjQvrJFzqygI99gL6Z4u2xctPW1tB8ErvA==", - "dependencies": { - "debug": "^4.3.1", - "js-sdsl": "4.3.0" - } - }, - "node_modules/object-hash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", - "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", - "engines": { - "node": ">= 6" - } - }, - "node_modules/object-inspect": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", - "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", - "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.defaults": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz", - "integrity": "sha512-c/K0mw/F11k4dEUBMW8naXUuBuhxRCfG7W+yFy8EcijU/rSmazOUd1XAEEe6bC0OuXY4HUKjTJv7xbxIMqdxrA==", - "dependencies": { - "array-each": "^1.0.1", - "array-slice": "^1.0.0", - "for-own": "^1.0.0", - "isobject": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object.fromentries": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.7.tgz", - "integrity": "sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.groupby": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.2.tgz", - "integrity": "sha512-bzBq58S+x+uo0VjurFT0UktpKHOZmv4/xePiOA1nbB9pMqpGK7rUPNgf+1YC+7mE+0HzhTMqNUuCqvKhj6FnBw==", - "dev": true, - "dependencies": { - "array.prototype.filter": "^1.0.3", - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", - "es-errors": "^1.0.0" - } - }, - "node_modules/object.map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz", - "integrity": "sha512-3+mAJu2PLfnSVGHwIWubpOFLscJANBKuB/6A4CxBstc4aqwQY0FWcsppuy4jU5GSB95yES5JHSI+33AWuS4k6w==", - "dependencies": { - "for-own": "^1.0.0", - "make-iterator": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==", - "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object.values": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.7.tgz", - "integrity": "sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/obliterator": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-2.0.4.tgz", - "integrity": "sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ==" - }, - "node_modules/on-exit-leak-free": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", - "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", - "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", - "dev": true, - "dependencies": { - "mimic-fn": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/open": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", - "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", - "dependencies": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", - "is-wsl": "^2.2.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/openapi3-ts": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/openapi3-ts/-/openapi3-ts-4.2.2.tgz", - "integrity": "sha512-+9g4actZKeb3czfi9gVQ4Br2Ju3KwhCAQJBNaKgye5KggqcBLIhFHH+nIkcm0BUX00TrAJl6dH4JWgM4G4JWrw==", - "dependencies": { - "yaml": "^2.3.4" - } - }, - "node_modules/optionator": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", - "dev": true, - "dependencies": { - "@aashutoshrathi/word-wrap": "^1.2.3", - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/ora": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-8.0.1.tgz", - "integrity": "sha512-ANIvzobt1rls2BDny5fWZ3ZVKyD6nscLvfFRpQgfWsythlcsVUC9kL0zq6j2Z5z9wwp1kd7wpsD/T9qNPVLCaQ==", - "dependencies": { - "chalk": "^5.3.0", - "cli-cursor": "^4.0.0", - "cli-spinners": "^2.9.2", - "is-interactive": "^2.0.0", - "is-unicode-supported": "^2.0.0", - "log-symbols": "^6.0.0", - "stdin-discarder": "^0.2.1", - "string-width": "^7.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/ora/node_modules/chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/ora/node_modules/cli-cursor": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", - "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", - "dependencies": { - "restore-cursor": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/node_modules/emoji-regex": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz", - "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==" - }, - "node_modules/ora/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==", - "engines": { - "node": ">=6" - } - }, - "node_modules/ora/node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/node_modules/restore-cursor": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", - "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/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==" - }, - "node_modules/ora/node_modules/string-width": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.1.0.tgz", - "integrity": "sha512-SEIJCWiX7Kg4c129n48aDRwLbFb2LJmXXFrWBG4NGaRtMQ3myKPKbwrD1BKqQn74oCoNMBVrfDEr5M9YxCsrkw==", - "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/p-cancelable": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", - "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/p-is-promise": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-3.0.0.tgz", - "integrity": "sha512-Wo8VsW4IRQSKVXsJCn7TomUaVtyfjVDn3nUP7kE967BQk0CwFpdbZs0X0uk5sW9mkBa9eNM7hCMaG93WUAwxYQ==", - "engines": { - "node": ">=8" - } - }, - "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": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-map": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-5.5.0.tgz", - "integrity": "sha512-VFqfGDHlx87K66yZrNdI4YGtD70IRyd+zSvgks6mzHPRNkoKy+9EKP4SFC77/vTTQYmRmti7dvqC+m5jBrBAcg==", - "dependencies": { - "aggregate-error": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/param-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", - "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", - "dependencies": { - "dot-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-filepath": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", - "integrity": "sha512-FwdRXKCohSVeXqwtYonZTXtbGJKrn+HNyWDYVcp5yuJlesTwNH4rsmRZ+GrKAPJ5bLpRxESMeS+Rl0VCHRvB2Q==", - "dependencies": { - "is-absolute": "^1.0.0", - "map-cache": "^0.2.0", - "path-root": "^0.1.1" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/parse-passwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", - "integrity": "sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pascal-case": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", - "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "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==" - }, - "node_modules/path-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/path-case/-/path-case-3.0.4.tgz", - "integrity": "sha512-qO4qCFjXqVTrcbPt/hQfhTQ+VhFsqNKOPtytgNKkKxSoEp3XPUQ8ObFuePylOIok5gjn69ry8XiULxCwot3Wfg==", - "dependencies": { - "dot-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "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==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "engines": { - "node": ">=8" - } - }, - "node_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==" - }, - "node_modules/path-root": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz", - "integrity": "sha512-QLcPegTHF11axjfojBIoDygmS2E3Lf+8+jI6wOVmNVenrKSo3mFdSGiIgdSHenczw3wPtlVMQaFVwGmM7BJdtg==", - "dependencies": { - "path-root-regex": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-root-regex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz", - "integrity": "sha512-4GlJ6rZDhQZFE0DPVKh0e9jmZ5egZfxTkp7bcRDuPlJXbAwhxcl2dINPUAsjLdejqaLsCeg8axcLjIbvBjN4pQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-scurry": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", - "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", - "dependencies": { - "lru-cache": "^9.1.1 || ^10.0.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/path-to-regexp": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.1.tgz", - "integrity": "sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==", - "dev": true - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "engines": { - "node": ">=8" - } - }, - "node_modules/pathe": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", - "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", - "dev": true - }, - "node_modules/pathval": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", - "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/peek-stream": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/peek-stream/-/peek-stream-1.1.3.tgz", - "integrity": "sha512-FhJ+YbOSBb9/rIl2ZeE/QHEsWn7PqNYt8ARAY3kIgNGOk13g9FGyIY6JIl/xB/3TFRVoTv5as0l11weORrTekA==", - "dependencies": { - "buffer-from": "^1.0.0", - "duplexify": "^3.5.0", - "through2": "^2.0.3" - } - }, - "node_modules/peek-stream/node_modules/duplexify": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", - "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", - "dependencies": { - "end-of-stream": "^1.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.0.0", - "stream-shift": "^1.0.0" - } - }, - "node_modules/peek-stream/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" - }, - "node_modules/peek-stream/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/peek-stream/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "node_modules/peek-stream/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/perfect-debounce": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", - "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", - "dev": true - }, - "node_modules/pg-int8": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", - "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/pg-protocol": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.0.tgz", - "integrity": "sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==" - }, - "node_modules/pg-types": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", - "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", - "dependencies": { - "pg-int8": "1.0.1", - "postgres-array": "~2.0.0", - "postgres-bytea": "~1.0.0", - "postgres-date": "~1.0.4", - "postgres-interval": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pino": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/pino/-/pino-8.19.0.tgz", - "integrity": "sha512-oswmokxkav9bADfJ2ifrvfHUwad6MLp73Uat0IkQWY3iAw5xTRoznXbXksZs8oaOUMpmhVWD+PZogNzllWpJaA==", - "dependencies": { - "atomic-sleep": "^1.0.0", - "fast-redact": "^3.1.1", - "on-exit-leak-free": "^2.1.0", - "pino-abstract-transport": "v1.1.0", - "pino-std-serializers": "^6.0.0", - "process-warning": "^3.0.0", - "quick-format-unescaped": "^4.0.3", - "real-require": "^0.2.0", - "safe-stable-stringify": "^2.3.1", - "sonic-boom": "^3.7.0", - "thread-stream": "^2.0.0" - }, - "bin": { - "pino": "bin.js" - } - }, - "node_modules/pino-abstract-transport": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.1.0.tgz", - "integrity": "sha512-lsleG3/2a/JIWUtf9Q5gUNErBqwIu1tUKTT3dUzaf5DySw9ra1wcqKjJjLX1VTY64Wk1eEOYsVGSaGfCK85ekA==", - "dependencies": { - "readable-stream": "^4.0.0", - "split2": "^4.0.0" - } - }, - "node_modules/pino-abstract-transport/node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "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": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "node_modules/pino-abstract-transport/node_modules/readable-stream": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", - "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", - "dependencies": { - "abort-controller": "^3.0.0", - "buffer": "^6.0.3", - "events": "^3.3.0", - "process": "^0.11.10", - "string_decoder": "^1.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/pino-loki": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/pino-loki/-/pino-loki-2.2.1.tgz", - "integrity": "sha512-NLo9INo4lOQ8PfC0i/AZBh8xh6LCCkuGRuREq69Mw25zmoISlZiYCn5FBidagu+Cjm/dvhvt19THRhc0B71NnA==", - "dependencies": { - "commander": "^10.0.1", - "got": "^11.8.6", - "pino-abstract-transport": "^1.1.0", - "pump": "^3.0.0" - }, - "bin": { - "pino-loki": "dist/cli.cjs" - }, - "funding": { - "url": "https://github.com/sponsors/Julien-R44" - } - }, - "node_modules/pino-pretty": { - "version": "10.3.1", - "resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-10.3.1.tgz", - "integrity": "sha512-az8JbIYeN/1iLj2t0jR9DV48/LQ3RC6hZPpapKPkb84Q+yTidMCpgWxIT3N0flnBDilyBQ1luWNpOeJptjdp/g==", - "dependencies": { - "colorette": "^2.0.7", - "dateformat": "^4.6.3", - "fast-copy": "^3.0.0", - "fast-safe-stringify": "^2.1.1", - "help-me": "^5.0.0", - "joycon": "^3.1.1", - "minimist": "^1.2.6", - "on-exit-leak-free": "^2.1.0", - "pino-abstract-transport": "^1.0.0", - "pump": "^3.0.0", - "readable-stream": "^4.0.0", - "secure-json-parse": "^2.4.0", - "sonic-boom": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "bin": { - "pino-pretty": "bin.js" - } - }, - "node_modules/pino-pretty/node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "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": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "node_modules/pino-pretty/node_modules/readable-stream": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", - "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", - "dependencies": { - "abort-controller": "^3.0.0", - "buffer": "^6.0.3", - "events": "^3.3.0", - "process": "^0.11.10", - "string_decoder": "^1.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/pino-std-serializers": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-6.2.2.tgz", - "integrity": "sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA==" - }, - "node_modules/pkg-types": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.0.3.tgz", - "integrity": "sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==", - "dev": true, - "dependencies": { - "jsonc-parser": "^3.2.0", - "mlly": "^1.2.0", - "pathe": "^1.1.0" - } - }, - "node_modules/plop": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/plop/-/plop-4.0.1.tgz", - "integrity": "sha512-5n8QU93kvL/ObOzBcPAB1siVFtAH1TZM6TntJ3JK5kXT0jIgnQV+j+uaOWWFJlg1cNkzLYm8klgASF65K36q9w==", - "dependencies": { - "@types/liftoff": "^4.0.3", - "chalk": "^5.3.0", - "interpret": "^3.1.1", - "liftoff": "^4.0.0", - "minimist": "^1.2.8", - "node-plop": "^0.32.0", - "ora": "^8.0.0", - "v8flags": "^4.0.1" - }, - "bin": { - "plop": "bin/plop.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/plop/node_modules/chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/polite-json": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/polite-json/-/polite-json-4.0.1.tgz", - "integrity": "sha512-8LI5ZeCPBEb4uBbcYKNVwk4jgqNx1yHReWoW4H4uUihWlSqZsUDfSITrRhjliuPgxsNPFhNSudGO2Zu4cbWinQ==", - "dev": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/possible-typed-array-names": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", - "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/postcss": { - "version": "8.4.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz", - "integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "nanoid": "^3.3.7", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/postgres-array": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", - "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", - "engines": { - "node": ">=4" - } - }, - "node_modules/postgres-bytea": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", - "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postgres-date": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", - "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postgres-interval": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", - "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", - "dependencies": { - "xtend": "^4.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/preact": { - "version": "10.19.6", - "resolved": "https://registry.npmjs.org/preact/-/preact-10.19.6.tgz", - "integrity": "sha512-gympg+T2Z1fG1unB8NH29yHJwnEaCH37Z32diPDku316OTnRPeMbiRV9kTrfZpocXjdfnWuFUl/Mj4BHaf6gnw==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/preact" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "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/prettier-linter-helpers": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", - "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", - "dev": true, - "dependencies": { - "fast-diff": "^1.1.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.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/process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", - "engines": { - "node": ">= 0.6.0" - } - }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, - "node_modules/process-warning": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-3.0.0.tgz", - "integrity": "sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==" - }, - "node_modules/proper-lockfile": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", - "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.2.4", - "retry": "^0.12.0", - "signal-exit": "^3.0.2" - } - }, - "node_modules/proper-lockfile/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/properties-reader": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/properties-reader/-/properties-reader-2.3.0.tgz", - "integrity": "sha512-z597WicA7nDZxK12kZqHr2TcvwNU1GCfA5UwfDY/HDp3hXPoPlb5rlEx9bwGTiJnc0OqbBTkU975jDToth8Gxw==", - "dev": true, - "dependencies": { - "mkdirp": "^1.0.4" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/steveukx/properties?sponsor=1" - } - }, - "node_modules/property-expr": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.6.tgz", - "integrity": "sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA==", - "dev": true - }, - "node_modules/proto3-json-serializer": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-2.0.1.tgz", - "integrity": "sha512-8awBvjO+FwkMd6gNoGFZyqkHZXCFd54CIYTb6De7dPaufGJ2XNW+QUNqbMr8MaAocMdb+KpsD4rxEOaTBDCffA==", - "dependencies": { - "protobufjs": "^7.2.5" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/protobufjs": { - "version": "7.2.6", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.6.tgz", - "integrity": "sha512-dgJaEDDL6x8ASUZ1YqWciTRrdOuYNzoOf27oHNfdyvKqHr5i0FV7FSLU+aIeFjyFgVxrpTOtQUi0BLLBymZaBw==", - "hasInstallScript": true, - "dependencies": { - "@protobufjs/aspromise": "^1.1.2", - "@protobufjs/base64": "^1.1.2", - "@protobufjs/codegen": "^2.0.4", - "@protobufjs/eventemitter": "^1.1.0", - "@protobufjs/fetch": "^1.1.0", - "@protobufjs/float": "^1.0.2", - "@protobufjs/inquire": "^1.1.0", - "@protobufjs/path": "^1.1.2", - "@protobufjs/pool": "^1.1.0", - "@protobufjs/utf8": "^1.1.0", - "@types/node": ">=13.7.0", - "long": "^5.0.0" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/pumpify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-2.0.1.tgz", - "integrity": "sha512-m7KOje7jZxrmutanlkS1daj1dS6z6BgslzOXmcSEpIlCxM3VJH7lG5QLeck/6hgF6F4crFf01UtQmNsJfweTAw==", - "dependencies": { - "duplexify": "^4.1.1", - "inherits": "^2.0.3", - "pump": "^3.0.0" - } - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/querystringify": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" - }, - "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==", - "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/queue-tick": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz", - "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==", - "dev": true - }, - "node_modules/quick-format-unescaped": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", - "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==" - }, - "node_modules/quick-lru": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", - "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "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/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/readdir-glob": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", - "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", - "dev": true, - "dependencies": { - "minimatch": "^5.1.0" - } - }, - "node_modules/readdir-glob/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/real-require": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", - "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==", - "engines": { - "node": ">= 12.13.0" - } - }, - "node_modules/rechoir": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", - "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", - "dependencies": { - "resolve": "^1.20.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" - }, - "node_modules/regexp.prototype.flags": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", - "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.6", - "define-properties": "^1.2.1", - "es-errors": "^1.3.0", - "set-function-name": "^2.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/regexparam": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/regexparam/-/regexparam-3.0.0.tgz", - "integrity": "sha512-RSYAtP31mvYLkAHrOlh25pCNQ5hWnT106VukGaaFfuJrZFkGRX5GhUAdPqpSDXxOhA2c4akmRuplv1mRqnBn6Q==", - "engines": { - "node": ">=8" - } - }, - "node_modules/regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, - "node_modules/reinterval": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reinterval/-/reinterval-1.1.0.tgz", - "integrity": "sha512-QIRet3SYrGp0HUHO88jVskiG6seqUGC5iAG7AwI/BV4ypGcuqk9Du6YQBUOUqm9c8pw1eyLoIaONifRua1lsEQ==" - }, - "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==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-in-the-middle": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-7.2.0.tgz", - "integrity": "sha512-3TLx5TGyAY6AOqLBoXmHkNql0HIf2RGbuMgCDT2WO/uGVAPJs6h7Kl+bN6TIZGd9bWhWPwnDnTHGtW8Iu77sdw==", - "dependencies": { - "debug": "^4.1.1", - "module-details-from-path": "^1.0.3", - "resolve": "^1.22.1" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" - }, - "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-alpn": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", - "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==" - }, - "node_modules/resolve-dir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", - "integrity": "sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==", - "dependencies": { - "expand-tilde": "^2.0.0", - "global-modules": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/resolve-import": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/resolve-import/-/resolve-import-1.4.5.tgz", - "integrity": "sha512-HXb4YqODuuXT7Icq1Z++0g2JmhgbUHSs3VT2xR83gqvAPUikYT2Xk+562KHQgiaNkbBOlPddYrDLsC44qQggzw==", - "dev": true, - "dependencies": { - "glob": "^10.3.3", - "walk-up-path": "^3.0.1" - }, - "engines": { - "node": "16 >=16.17.0 || 18 >= 18.6.0 || >=20" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "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/responselike": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", - "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", - "dependencies": { - "lowercase-keys": "^2.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/restore-cursor/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==", - "engines": { - "node": ">=6" - } - }, - "node_modules/restore-cursor/node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/restore-cursor/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==" - }, - "node_modules/ret": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.2.2.tgz", - "integrity": "sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==", - "engines": { - "node": ">=4" - } - }, - "node_modules/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/retry-request": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-7.0.2.tgz", - "integrity": "sha512-dUOvLMJ0/JJYEn8NrpOaGNE7X3vpI5XlZS/u0ANjqtcZVKnIxP7IgCFwrKTxENw29emmwug53awKtaMm4i9g5w==", - "dependencies": { - "@types/request": "^2.48.8", - "extend": "^3.0.2", - "teeny-request": "^9.0.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rfdc": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.1.tgz", - "integrity": "sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==" - }, - "node_modules/rimraf": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.5.tgz", - "integrity": "sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A==", - "dev": true, - "dependencies": { - "glob": "^10.3.7" - }, - "bin": { - "rimraf": "dist/esm/bin.mjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/rollup": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.12.0.tgz", - "integrity": "sha512-wz66wn4t1OHIJw3+XU7mJJQV/2NAfw5OAk6G6Hoo3zcvz/XOfQ52Vgi+AN4Uxoxi0KBBwk2g8zPrTDA4btSB/Q==", - "dev": true, - "dependencies": { - "@types/estree": "1.0.5" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.12.0", - "@rollup/rollup-android-arm64": "4.12.0", - "@rollup/rollup-darwin-arm64": "4.12.0", - "@rollup/rollup-darwin-x64": "4.12.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.12.0", - "@rollup/rollup-linux-arm64-gnu": "4.12.0", - "@rollup/rollup-linux-arm64-musl": "4.12.0", - "@rollup/rollup-linux-riscv64-gnu": "4.12.0", - "@rollup/rollup-linux-x64-gnu": "4.12.0", - "@rollup/rollup-linux-x64-musl": "4.12.0", - "@rollup/rollup-win32-arm64-msvc": "4.12.0", - "@rollup/rollup-win32-ia32-msvc": "4.12.0", - "@rollup/rollup-win32-x64-msvc": "4.12.0", - "fsevents": "~2.3.2" - } - }, - "node_modules/run-async": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-3.0.0.tgz", - "integrity": "sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==", - "engines": { - "node": ">=0.12.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==", - "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/rxjs": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", - "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/safe-array-concat": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.0.tgz", - "integrity": "sha512-ZdQ0Jeb9Ofti4hbt5lX3T2JcAamT9hfzYU1MNB+z/jaEbB6wfFfPIR/zEORmZqobkCCJhSjodobH6WHNmJ97dg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.5", - "get-intrinsic": "^1.2.2", - "has-symbols": "^1.0.3", - "isarray": "^2.0.5" - }, - "engines": { - "node": ">=0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "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/safe-regex-test": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", - "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "is-regex": "^1.1.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safe-regex2": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-2.0.0.tgz", - "integrity": "sha512-PaUSFsUaNNuKwkBijoAPHAK6/eM6VirvyPWlZ7BAQy4D+hCvh4B6lIG+nPdhbFfIbP+gTGBcrdsOaUs0F+ZBOQ==", - "dependencies": { - "ret": "~0.2.0" - } - }, - "node_modules/safe-stable-stringify": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz", - "integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==", - "engines": { - "node": ">=10" - } - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "node_modules/sax": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz", - "integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==", - "dev": true - }, - "node_modules/schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", - "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/search-insights": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.13.0.tgz", - "integrity": "sha512-Orrsjf9trHHxFRuo9/rzm0KIWmgzE8RMlZMzuhZOJ01Rnz3D0YBAe+V6473t6/H6c7irs6Lt48brULAiRWb3Vw==", - "dev": true, - "peer": true - }, - "node_modules/section-matter": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz", - "integrity": "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==", - "dev": true, - "dependencies": { - "extend-shallow": "^2.0.1", - "kind-of": "^6.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/secure-json-parse": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz", - "integrity": "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==" - }, - "node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver/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==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/sentence-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/sentence-case/-/sentence-case-3.0.4.tgz", - "integrity": "sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg==", - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3", - "upper-case-first": "^2.0.2" - } - }, - "node_modules/serialize-javascript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", - "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "node_modules/set-cookie-parser": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.6.0.tgz", - "integrity": "sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==" - }, - "node_modules/set-function-length": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.1.tgz", - "integrity": "sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==", - "dependencies": { - "define-data-property": "^1.1.2", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.3", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/set-function-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", - "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", - "dev": true, - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" - }, - "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==", - "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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/shiki": { - "version": "0.14.7", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.7.tgz", - "integrity": "sha512-dNPAPrxSc87ua2sKJ3H5dQ/6ZaY8RNnaAqK+t0eG7p0Soi2ydiqbGOTaZCqaYvA/uZYfS1LJnemt3Q+mSfcPCg==", - "dev": true, - "dependencies": { - "ansi-sequence-parser": "^1.1.0", - "jsonc-parser": "^3.2.0", - "vscode-oniguruma": "^1.7.0", - "vscode-textmate": "^8.0.0" - } - }, - "node_modules/shimmer": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz", - "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==" - }, - "node_modules/side-channel": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.5.tgz", - "integrity": "sha512-QcgiIWV4WV7qWExbN5llt6frQB/lBven9pqliLXfGPB+K9ZYXxDozp0wLkHS24kWCm+6YXH/f0HhnObZnZOBnQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "object-inspect": "^1.13.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/siginfo": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", - "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", - "dev": true - }, - "node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/sinon": { - "version": "17.0.1", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-17.0.1.tgz", - "integrity": "sha512-wmwE19Lie0MLT+ZYNpDymasPHUKTaZHUH/pKEubRXIzySv9Atnlw+BUMGCzWgV7b7wO+Hw6f1TEOr0IUnmU8/g==", - "dev": true, - "dependencies": { - "@sinonjs/commons": "^3.0.0", - "@sinonjs/fake-timers": "^11.2.2", - "@sinonjs/samsam": "^8.0.0", - "diff": "^5.1.0", - "nise": "^5.1.5", - "supports-color": "^7.2.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/sinon" - } - }, - "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/snake-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", - "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", - "dependencies": { - "dot-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/sonic-boom": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.8.0.tgz", - "integrity": "sha512-ybz6OYOUjoQQCQ/i4LU8kaToD8ACtYP+Cj5qd2AO36bwbdewxWJ3ArmJ2cr6AvxlL2o0PqnCcPGUgkILbfkaCA==", - "dependencies": { - "atomic-sleep": "^1.0.0" - } - }, - "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==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-loader": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-4.0.2.tgz", - "integrity": "sha512-oYwAqCuL0OZhBoSgmdrLa7mv9MjommVMiQIWgcztf+eS4+8BfcUee6nenFnDhKOhzAVnk5gpZdfnz1iiBv+5sg==", - "dependencies": { - "iconv-lite": "^0.6.3", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": ">= 14.15.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.72.1" - } - }, - "node_modules/source-map-loader/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/speakingurl": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz", - "integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/split-ca": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/split-ca/-/split-ca-1.0.1.tgz", - "integrity": "sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ==", - "dev": true - }, - "node_modules/split2": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", - "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", - "engines": { - "node": ">= 10.x" - } - }, - "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/ssh-remote-port-forward": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/ssh-remote-port-forward/-/ssh-remote-port-forward-1.0.4.tgz", - "integrity": "sha512-x0LV1eVDwjf1gmG7TTnfqIzf+3VPRz7vrNIjX6oYLbeCrf/PeVY6hkT68Mg+q02qXxQhrLjB0jfgvhevoCRmLQ==", - "dev": true, - "dependencies": { - "@types/ssh2": "^0.5.48", - "ssh2": "^1.4.0" - } - }, - "node_modules/ssh-remote-port-forward/node_modules/@types/ssh2": { - "version": "0.5.52", - "resolved": "https://registry.npmjs.org/@types/ssh2/-/ssh2-0.5.52.tgz", - "integrity": "sha512-lbLLlXxdCZOSJMCInKH2+9V/77ET2J6NPQHpFI0kda61Dd1KglJs+fPQBchizmzYSOJBgdTajhPqBO1xxLywvg==", - "dev": true, - "dependencies": { - "@types/node": "*", - "@types/ssh2-streams": "*" - } - }, - "node_modules/ssh2": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.15.0.tgz", - "integrity": "sha512-C0PHgX4h6lBxYx7hcXwu3QWdh4tg6tZZsTfXcdvc5caW/EMxaB4H9dWsl7qk+F7LAW762hp8VbXOX7x4xUYvEw==", - "dev": true, - "hasInstallScript": true, - "dependencies": { - "asn1": "^0.2.6", - "bcrypt-pbkdf": "^1.0.2" - }, - "engines": { - "node": ">=10.16.0" - }, - "optionalDependencies": { - "cpu-features": "~0.0.9", - "nan": "^2.18.0" - } - }, - "node_modules/stackback": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", - "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", - "dev": true - }, - "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/std-env": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.7.0.tgz", - "integrity": "sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==", - "dev": true - }, - "node_modules/stdin-discarder": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", - "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/stoppable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/stoppable/-/stoppable-1.1.0.tgz", - "integrity": "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==", - "engines": { - "node": ">=4", - "npm": ">=6" - } - }, - "node_modules/stream-events": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz", - "integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==", - "dependencies": { - "stubs": "^3.0.0" - } - }, - "node_modules/stream-shift": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz", - "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==" - }, - "node_modules/streamx": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.16.1.tgz", - "integrity": "sha512-m9QYj6WygWyWa3H1YY69amr4nVgy61xfjys7xO7kviL5rfIEc2naf+ewFiOA+aEJD7y0JO3h2GoiUv4TDwEGzQ==", - "dev": true, - "dependencies": { - "fast-fifo": "^1.1.0", - "queue-tick": "^1.0.1" - }, - "optionalDependencies": { - "bare-events": "^2.2.0" - } - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/string-width-cjs": { - "name": "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==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/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==" - }, - "node_modules/string-width/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/string-width/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/string.prototype.trim": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", - "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimend": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", - "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz", - "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/strip-bom-string": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", - "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/strip-final-newline": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", - "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/strip-literal": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-2.0.0.tgz", - "integrity": "sha512-f9vHgsCWBq2ugHAkGMiiYY+AYG0D/cbloKKg0nhaaaSNsujdGIpVXCNsrJpCKr5M0f4aI31mr13UjY6GAuXCKA==", - "dev": true, - "dependencies": { - "js-tokens": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/strnum": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", - "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==" - }, - "node_modules/stubs": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz", - "integrity": "sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw==" - }, - "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==", - "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==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/swagger-ui-dist": { - "version": "5.11.8", - "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.11.8.tgz", - "integrity": "sha512-IfPtCPdf6opT5HXrzHO4kjL1eco0/8xJCtcs7ilhKuzatrpF2j9s+3QbOag6G3mVFKf+g+Ca5UG9DquVUs2obA==" - }, - "node_modules/swc-loader": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/swc-loader/-/swc-loader-0.2.6.tgz", - "integrity": "sha512-9Zi9UP2YmDpgmQVbyOPJClY0dwf58JDyDMQ7uRc4krmc72twNI2fvlBWHLqVekBpPc7h5NJkGVT1zNDxFrqhvg==", - "dependencies": { - "@swc/counter": "^0.1.3" - }, - "peerDependencies": { - "@swc/core": "^1.2.147", - "webpack": ">=2" - } - }, - "node_modules/sync-content": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/sync-content/-/sync-content-1.0.2.tgz", - "integrity": "sha512-znd3rYiiSxU3WteWyS9a6FXkTA/Wjk8WQsOyzHbineeL837dLn3DA4MRhsIX3qGcxDMH6+uuFV4axztssk7wEQ==", - "dev": true, - "dependencies": { - "glob": "^10.2.6", - "mkdirp": "^3.0.1", - "path-scurry": "^1.9.2", - "rimraf": "^5.0.1" - }, - "bin": { - "sync-content": "dist/mjs/bin.mjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/sync-content/node_modules/mkdirp": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", - "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", - "dev": true, - "bin": { - "mkdirp": "dist/cjs/src/bin.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/synckit": { - "version": "0.8.8", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.8.tgz", - "integrity": "sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==", - "dev": true, - "dependencies": { - "@pkgr/core": "^0.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/unts" - } - }, - "node_modules/tabbable": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", - "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==", - "dev": true - }, - "node_modules/tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/tar-fs": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.5.tgz", - "integrity": "sha512-JOgGAmZyMgbqpLwct7ZV8VzkEB6pxXFBVErLtb+XCOqzc6w1xiWKI9GVd6bwk68EX7eJ4DWmfXVmq8K2ziZTGg==", - "dev": true, - "dependencies": { - "pump": "^3.0.0", - "tar-stream": "^3.1.5" - }, - "optionalDependencies": { - "bare-fs": "^2.1.1", - "bare-path": "^2.1.0" - } - }, - "node_modules/tar-fs/node_modules/tar-stream": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", - "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", - "dev": true, - "dependencies": { - "b4a": "^1.6.4", - "fast-fifo": "^1.2.0", - "streamx": "^2.15.0" - } - }, - "node_modules/tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", - "dev": true, - "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/teeny-request": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-9.0.0.tgz", - "integrity": "sha512-resvxdc6Mgb7YEThw6G6bExlXKkv6+YbuzGg9xuXxSgxJF7Ozs+o8Y9+2R3sArdWdW8nOokoQb1yrpFB0pQK2g==", - "dependencies": { - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.0", - "node-fetch": "^2.6.9", - "stream-events": "^1.0.5", - "uuid": "^9.0.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/terser": { - "version": "5.28.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.28.1.tgz", - "integrity": "sha512-wM+bZp54v/E9eRRGXb5ZFDvinrJIOaTapx3WUokyVGZu5ucVCK55zEgGd5Dl2fSr3jUo5sDiERErUWLY6QPFyA==", - "dependencies": { - "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.8.2", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/terser-webpack-plugin": { - "version": "5.3.10", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", - "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.20", - "jest-worker": "^27.4.5", - "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.1", - "terser": "^5.26.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "uglify-js": { - "optional": true - } - } - }, - "node_modules/terser/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" - }, - "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/test-exclude/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/test-exclude/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/test-exclude/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/testcontainers": { - "version": "10.7.1", - "resolved": "https://registry.npmjs.org/testcontainers/-/testcontainers-10.7.1.tgz", - "integrity": "sha512-JarbT6o7fv1siUts4tGv3wBoYrWKxjla69+5QWG9+bcd4l+ECJ3ikfGD/hpXRmRBsnjzeWyV+tL9oWOBRzk+lA==", - "dev": true, - "dependencies": { - "@balena/dockerignore": "^1.0.2", - "@types/dockerode": "^3.3.21", - "archiver": "^5.3.2", - "async-lock": "^1.4.0", - "byline": "^5.0.0", - "debug": "^4.3.4", - "docker-compose": "^0.24.2", - "dockerode": "^3.3.5", - "get-port": "^5.1.1", - "node-fetch": "^2.7.0", - "proper-lockfile": "^4.1.2", - "properties-reader": "^2.3.0", - "ssh-remote-port-forward": "^1.0.4", - "tar-fs": "^3.0.4", - "tmp": "^0.2.1" - } - }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true - }, - "node_modules/thread-stream": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-2.4.1.tgz", - "integrity": "sha512-d/Ex2iWd1whipbT681JmTINKw0ZwOUBZm7+Gjs64DHuX34mmw8vJL2bFAaNacaW72zYiTJxSHi5abUuOi5nsfg==", - "dependencies": { - "real-require": "^0.2.0" - } - }, - "node_modules/through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "dependencies": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - }, - "node_modules/through2/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" - }, - "node_modules/through2/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/through2/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "node_modules/through2/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/tiny-case": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tiny-case/-/tiny-case-1.0.3.tgz", - "integrity": "sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q==", - "dev": true - }, - "node_modules/tinybench": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.6.0.tgz", - "integrity": "sha512-N8hW3PG/3aOoZAN5V/NSAEDz0ZixDSSt5b/a05iqtpgfLWMSVuCo7w0k2vVvEjdrIoeGqZzweX2WlyioNIHchA==", - "dev": true - }, - "node_modules/tinypool": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.2.tgz", - "integrity": "sha512-SUszKYe5wgsxnNOVlBYO6IC+8VGWdVGZWAqUxp3UErNBtptZvWbwyUOyzNL59zigz2rCA92QiL3wvG+JDSdJdQ==", - "dev": true, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/tinyspy": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.2.1.tgz", - "integrity": "sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==", - "dev": true, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/title-case": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/title-case/-/title-case-3.0.3.tgz", - "integrity": "sha512-e1zGYRvbffpcHIrnuqT0Dh+gEJtDaxDSoG4JAIpq4oDFyooziLBIiYQv0GBT4FUAnUop5uZ1hiIAj7oAF6sOCA==", - "dependencies": { - "tslib": "^2.0.3" - } - }, - "node_modules/tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", - "dev": true, - "dependencies": { - "rimraf": "^3.0.0" - }, - "engines": { - "node": ">=8.17.0" - } - }, - "node_modules/tmp/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/tmp/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/tmp/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/tmp/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "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==", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/toad-cache": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/toad-cache/-/toad-cache-3.7.0.tgz", - "integrity": "sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==", - "engines": { - "node": ">=12" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/toposort": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz", - "integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==", - "dev": true - }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, - "node_modules/trouter": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/trouter/-/trouter-4.0.0.tgz", - "integrity": "sha512-bwwr76BThfiVwAFZqks5cJ+VoKNM3/2Yg1ZwJslkdmAUQ6S0UNoCoGYFDxdw+u1skfexggdmD2p35kW5Td4Cug==", - "dependencies": { - "regexparam": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/ts-api-utils": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.2.1.tgz", - "integrity": "sha512-RIYA36cJn2WiH9Hy77hdF9r7oEwxAtB/TS9/S4Qd90Ap4z5FSiin5zEiTL44OII1Y3IIlEvxwxFUVgrHSZ/UpA==", - "dev": true, - "engines": { - "node": ">=16" - }, - "peerDependencies": { - "typescript": ">=4.2.0" - } - }, - "node_modules/ts-deepmerge": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/ts-deepmerge/-/ts-deepmerge-7.0.0.tgz", - "integrity": "sha512-WZ/iAJrKDhdINv1WG6KZIGHrZDar6VfhftG1QJFpVbOYZMYJLJOvZOo1amictRXVdBXZIgBHKswMTXzElngprA==", - "engines": { - "node": ">=14.13.1" - } - }, - "node_modules/ts-morph": { - "version": "21.0.1", - "resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-21.0.1.tgz", - "integrity": "sha512-dbDtVdEAncKctzrVZ+Nr7kHpHkv+0JDJb2MjjpBaj8bFeCkePU9rHfMklmhuLFnpeq/EJZk2IhStY6NzqgjOkg==", - "dependencies": { - "@ts-morph/common": "~0.22.0", - "code-block-writer": "^12.0.0" - } - }, - "node_modules/ts-node": { - "version": "10.9.2", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", - "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", - "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-node/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/tsconfig-paths": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", - "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", - "dev": true, - "dependencies": { - "@types/json5": "^0.0.29", - "json5": "^1.0.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - } - }, - "node_modules/tshy": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/tshy/-/tshy-1.11.1.tgz", - "integrity": "sha512-AzATR8weBaUW46Nh4B1k5cfxVuADKJTXe95xHh7BzcI1RjQQy6HeUXQDY+erGEGTLpiv6N6xMFmtEsMMc7x40Q==", - "dev": true, - "dependencies": { - "chalk": "^5.3.0", - "chokidar": "^3.5.3", - "foreground-child": "^3.1.1", - "mkdirp": "^3.0.1", - "polite-json": "^4.0.1", - "resolve-import": "^1.4.4", - "rimraf": "^5.0.1", - "sync-content": "^1.0.2", - "typescript": "5.2 || 5.3", - "walk-up-path": "^3.0.1" - }, - "bin": { - "tshy": "dist/esm/index.js" - }, - "engines": { - "node": "16 >=16.17 || 18 >=18.15.0 || >=20.6.1" - } - }, - "node_modules/tshy/node_modules/chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", - "dev": true, - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/tshy/node_modules/mkdirp": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", - "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", - "dev": true, - "bin": { - "mkdirp": "dist/cjs/src/bin.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" - }, - "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/tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", - "dev": true - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "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.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typed-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", - "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/typed-array-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", - "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-byte-offset": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", - "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", - "dev": true, - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-length": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.5.tgz", - "integrity": "sha512-yMi0PlwuznKHxKmcpoOdeLwxBoVPkqZxd7q2FgMkmD3bNwvF5VW0+UlUQ1k1vmktTu4Yu13Q0RIxEP8+B+wloA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13", - "possible-typed-array-names": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" - }, - "node_modules/typedoc": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.25.8.tgz", - "integrity": "sha512-mh8oLW66nwmeB9uTa0Bdcjfis+48bAjSH3uqdzSuSawfduROQLlXw//WSNZLYDdhmMVB7YcYZicq6e8T0d271A==", - "dev": true, - "dependencies": { - "lunr": "^2.3.9", - "marked": "^4.3.0", - "minimatch": "^9.0.3", - "shiki": "^0.14.7" - }, - "bin": { - "typedoc": "bin/typedoc" - }, - "engines": { - "node": ">= 16" - }, - "peerDependencies": { - "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x || 5.3.x" - } - }, - "node_modules/typedoc-plugin-markdown": { - "version": "3.17.1", - "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-3.17.1.tgz", - "integrity": "sha512-QzdU3fj0Kzw2XSdoL15ExLASt2WPqD7FbLeaqwT70+XjKyTshBnUlQA5nNREO1C2P8Uen0CDjsBLMsCQ+zd0lw==", - "dev": true, - "dependencies": { - "handlebars": "^4.7.7" - }, - "peerDependencies": { - "typedoc": ">=0.24.0" - } - }, - "node_modules/typescript": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", - "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/ufo": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.4.0.tgz", - "integrity": "sha512-Hhy+BhRBleFjpJ2vchUNN40qgkh0366FWJGqVLYBHev0vpHTrXSA0ryT+74UiW6KWsldNurQMKGqCm1M2zBciQ==", - "dev": true - }, - "node_modules/uglify-js": { - "version": "3.17.4", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", - "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", - "optional": true, - "bin": { - "uglifyjs": "bin/uglifyjs" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/unc-path-regex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", - "integrity": "sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" - }, - "node_modules/unionfs": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/unionfs/-/unionfs-4.5.1.tgz", - "integrity": "sha512-hn8pzkh0/80mpsIT/YBJKa4+BF/9pNh0IgysBi0CjL95Uok8Hus69TNfgeJckoUNwfTpBq26+F7edO1oBINaIw==", - "dependencies": { - "fs-monkey": "^1.0.0" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", - "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/upper-case": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-2.0.2.tgz", - "integrity": "sha512-KgdgDGJt2TpuwBUIjgG6lzw2GWFRCW9Qkfkiv0DxqHHLYJHmtmdUIKcZd8rHgFSjopVTlw6ggzCm1b8MFQwikg==", - "dependencies": { - "tslib": "^2.0.3" - } - }, - "node_modules/upper-case-first": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-2.0.2.tgz", - "integrity": "sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg==", - "dependencies": { - "tslib": "^2.0.3" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/url-parse": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", - "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", - "dependencies": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" - } - }, - "node_modules/util": { - "version": "0.12.5", - "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", - "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", - "dependencies": { - "inherits": "^2.0.3", - "is-arguments": "^1.0.4", - "is-generator-function": "^1.0.7", - "is-typed-array": "^1.1.3", - "which-typed-array": "^1.1.2" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" - }, - "node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "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.2.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz", - "integrity": "sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^2.0.0" - }, - "engines": { - "node": ">=10.12.0" - } - }, - "node_modules/v8flags": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-4.0.1.tgz", - "integrity": "sha512-fcRLaS4H/hrZk9hYwbdRM35D0U8IYMfEClhXxCivOojl+yTRAZH3Zy2sSy6qVCiGbV9YAtPssP6jaChqC9vPCg==", - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/vanilla-cookieconsent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/vanilla-cookieconsent/-/vanilla-cookieconsent-3.0.0.tgz", - "integrity": "sha512-oeK7FivRDb424mt3/UT8DG98Pu5m6Uuye7JsdzO6HnYkstW5QHXhkslXyUpE3phtKz3NEeIo7hzLUtfRNRMfbQ==" - }, - "node_modules/vite": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.4.tgz", - "integrity": "sha512-n+MPqzq+d9nMVTKyewqw6kSt+R3CkvF9QAKY8obiQn8g1fwTscKxyfaYnC632HtBXAQGc1Yjomphwn1dtwGAHg==", - "dev": true, - "dependencies": { - "esbuild": "^0.19.3", - "postcss": "^8.4.35", - "rollup": "^4.2.0" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^18.0.0 || >=20.0.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.4.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - } - } - }, - "node_modules/vite-node": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.3.1.tgz", - "integrity": "sha512-azbRrqRxlWTJEVbzInZCTchx0X69M/XPTCz4H+TLvlTcR/xH/3hkRqhOakT41fMJCMzXTu4UvegkZiEoJAWvng==", - "dev": true, - "dependencies": { - "cac": "^6.7.14", - "debug": "^4.3.4", - "pathe": "^1.1.1", - "picocolors": "^1.0.0", - "vite": "^5.0.0" - }, - "bin": { - "vite-node": "vite-node.mjs" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/vitepress": { - "version": "1.0.0-rc.44", - "resolved": "https://registry.npmjs.org/vitepress/-/vitepress-1.0.0-rc.44.tgz", - "integrity": "sha512-tO5taxGI7fSpBK1D8zrZTyJJERlyU9nnt0jHSt3fywfq3VKn977Hg0wUuTkEmwXlFYwuW26+6+3xorf4nD3XvA==", - "dev": true, - "dependencies": { - "@docsearch/css": "^3.5.2", - "@docsearch/js": "^3.5.2", - "@shikijs/core": "^1.1.5", - "@shikijs/transformers": "^1.1.5", - "@types/markdown-it": "^13.0.7", - "@vitejs/plugin-vue": "^5.0.4", - "@vue/devtools-api": "^7.0.14", - "@vueuse/core": "^10.7.2", - "@vueuse/integrations": "^10.7.2", - "focus-trap": "^7.5.4", - "mark.js": "8.11.1", - "minisearch": "^6.3.0", - "shiki": "^1.1.5", - "vite": "^5.1.3", - "vue": "^3.4.19" - }, - "bin": { - "vitepress": "bin/vitepress.js" - }, - "peerDependencies": { - "markdown-it-mathjax3": "^4.3.2", - "postcss": "^8.4.35" - }, - "peerDependenciesMeta": { - "markdown-it-mathjax3": { - "optional": true - }, - "postcss": { - "optional": true - } - } - }, - "node_modules/vitepress-sidebar": { - "version": "1.18.6", - "resolved": "https://registry.npmjs.org/vitepress-sidebar/-/vitepress-sidebar-1.18.6.tgz", - "integrity": "sha512-nXdaZGU28kHKsAnaDM8hQdofwLkm3sURJ21z/vmDKin6884ONx31ctTAG4ZcA5ROGuqpcvxrKxOiSOlfDS7BZg==", - "dev": true, - "dependencies": { - "gray-matter": "^4.0.3" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/vitepress/node_modules/shiki": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.1.7.tgz", - "integrity": "sha512-9kUTMjZtcPH3i7vHunA6EraTPpPOITYTdA5uMrvsJRexktqP0s7P3s9HVK80b4pP42FRVe03D7fT3NmJv2yYhw==", - "dev": true, - "dependencies": { - "@shikijs/core": "1.1.7" - } - }, - "node_modules/vitest": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.3.1.tgz", - "integrity": "sha512-/1QJqXs8YbCrfv/GPQ05wAZf2eakUPLPa18vkJAKE7RXOKfVHqMZZ1WlTjiwl6Gcn65M5vpNUB6EFLnEdRdEXQ==", - "dev": true, - "dependencies": { - "@vitest/expect": "1.3.1", - "@vitest/runner": "1.3.1", - "@vitest/snapshot": "1.3.1", - "@vitest/spy": "1.3.1", - "@vitest/utils": "1.3.1", - "acorn-walk": "^8.3.2", - "chai": "^4.3.10", - "debug": "^4.3.4", - "execa": "^8.0.1", - "local-pkg": "^0.5.0", - "magic-string": "^0.30.5", - "pathe": "^1.1.1", - "picocolors": "^1.0.0", - "std-env": "^3.5.0", - "strip-literal": "^2.0.0", - "tinybench": "^2.5.1", - "tinypool": "^0.8.2", - "vite": "^5.0.0", - "vite-node": "1.3.1", - "why-is-node-running": "^2.2.2" - }, - "bin": { - "vitest": "vitest.mjs" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "@edge-runtime/vm": "*", - "@types/node": "^18.0.0 || >=20.0.0", - "@vitest/browser": "1.3.1", - "@vitest/ui": "1.3.1", - "happy-dom": "*", - "jsdom": "*" - }, - "peerDependenciesMeta": { - "@edge-runtime/vm": { - "optional": true - }, - "@types/node": { - "optional": true - }, - "@vitest/browser": { - "optional": true - }, - "@vitest/ui": { - "optional": true - }, - "happy-dom": { - "optional": true - }, - "jsdom": { - "optional": true - } - } - }, - "node_modules/vscode-json-languageservice": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-4.2.1.tgz", - "integrity": "sha512-xGmv9QIWs2H8obGbWg+sIPI/3/pFgj/5OWBhNzs00BkYQ9UaB2F6JJaGB/2/YOZJ3BvLXQTC4Q7muqU25QgAhA==", - "dev": true, - "dependencies": { - "jsonc-parser": "^3.0.0", - "vscode-languageserver-textdocument": "^1.0.3", - "vscode-languageserver-types": "^3.16.0", - "vscode-nls": "^5.0.0", - "vscode-uri": "^3.0.3" - } - }, - "node_modules/vscode-languageserver-textdocument": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.11.tgz", - "integrity": "sha512-X+8T3GoiwTVlJbicx/sIAF+yuJAqz8VvwJyoMVhwEMoEKE/fkDmrqUgDMyBECcM2A2frVZIUj5HI/ErRXCfOeA==", - "dev": true - }, - "node_modules/vscode-languageserver-types": { - "version": "3.17.5", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", - "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==", - "dev": true - }, - "node_modules/vscode-nls": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-5.2.0.tgz", - "integrity": "sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng==", - "dev": true - }, - "node_modules/vscode-oniguruma": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz", - "integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==", - "dev": true - }, - "node_modules/vscode-textmate": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-8.0.0.tgz", - "integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==", - "dev": true - }, - "node_modules/vscode-uri": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz", - "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==", - "dev": true - }, - "node_modules/vue": { - "version": "3.4.19", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.19.tgz", - "integrity": "sha512-W/7Fc9KUkajFU8dBeDluM4sRGc/aa4YJnOYck8dkjgZoXtVsn3OeTGni66FV1l3+nvPA7VBFYtPioaGKUmEADw==", - "dev": true, - "dependencies": { - "@vue/compiler-dom": "3.4.19", - "@vue/compiler-sfc": "3.4.19", - "@vue/runtime-dom": "3.4.19", - "@vue/server-renderer": "3.4.19", - "@vue/shared": "3.4.19" - }, - "peerDependencies": { - "typescript": "*" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/walk-up-path": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/walk-up-path/-/walk-up-path-3.0.1.tgz", - "integrity": "sha512-9YlCL/ynK3CTlrSRrDxZvUauLzAswPCrsaCgilqFevUYpeEW0/3ScEjaa3kbW/T0ghhkEr7mv+fpjqn1Y1YuTA==", - "dev": true - }, - "node_modules/watchpack": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", - "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", - "dependencies": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", - "dependencies": { - "defaults": "^1.0.3" - } - }, - "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/webpack": { - "version": "5.90.3", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.90.3.tgz", - "integrity": "sha512-h6uDYlWCctQRuXBs1oYpVe6sFcWedl0dpcVaTf/YF67J9bKvwJajFulMVSYKHrksMB3I/pIagRzDxwxkebuzKA==", - "dependencies": { - "@types/eslint-scope": "^3.7.3", - "@types/estree": "^1.0.5", - "@webassemblyjs/ast": "^1.11.5", - "@webassemblyjs/wasm-edit": "^1.11.5", - "@webassemblyjs/wasm-parser": "^1.11.5", - "acorn": "^8.7.1", - "acorn-import-assertions": "^1.9.0", - "browserslist": "^4.21.10", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.15.0", - "es-module-lexer": "^1.2.1", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.9", - "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.2.0", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^3.2.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.10", - "watchpack": "^2.4.0", - "webpack-sources": "^3.2.3" - }, - "bin": { - "webpack": "bin/webpack.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependenciesMeta": { - "webpack-cli": { - "optional": true - } - } - }, - "node_modules/webpack-sources": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", - "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/webpack/node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/webpack/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "engines": { - "node": ">=4.0" - } - }, - "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==", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "dev": true, - "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-typed-array": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.14.tgz", - "integrity": "sha512-VnXFiIW8yNn9kIHN88xvZ4yOWchftKDsRJ8fEPacX/wl1lOvBrhsJ/OeJCXq7B0AaijRuqgzSKalJoPk+D8MPg==", - "dependencies": { - "available-typed-arrays": "^1.0.6", - "call-bind": "^1.0.5", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/why-is-node-running": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.2.2.tgz", - "integrity": "sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==", - "dev": true, - "dependencies": { - "siginfo": "^2.0.0", - "stackback": "0.0.2" - }, - "bin": { - "why-is-node-running": "cli.js" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==" - }, - "node_modules/worker-timers": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/worker-timers/-/worker-timers-7.1.2.tgz", - "integrity": "sha512-iqhXt5+Mc3u2nHj3G/w/E9pXqhlueniA2NlyelB/MQSHQuuW2fmmZGkveAv6yi4SSZvrpbveBBlqPSZ0MDCLww==", - "dependencies": { - "@babel/runtime": "^7.23.9", - "tslib": "^2.6.2", - "worker-timers-broker": "^6.1.2", - "worker-timers-worker": "^7.0.66" - } - }, - "node_modules/worker-timers-broker": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/worker-timers-broker/-/worker-timers-broker-6.1.2.tgz", - "integrity": "sha512-slFupigW5vtkGJ1VBCxYPwXFFRmvfioh02bCltBhbMkt3fFnkAbKBCg61pNTetlD0RAsP09mqx/FB0f4UMoHNw==", - "dependencies": { - "@babel/runtime": "^7.23.9", - "fast-unique-numbers": "^9.0.0", - "tslib": "^2.6.2", - "worker-timers-worker": "^7.0.66" - } - }, - "node_modules/worker-timers-worker": { - "version": "7.0.66", - "resolved": "https://registry.npmjs.org/worker-timers-worker/-/worker-timers-worker-7.0.66.tgz", - "integrity": "sha512-VCLa0H5K9fE2DVI/9r5zDuFrMQIpNL3UD/h4Ui49fIiRBTgv1Sqe0RM12brr83anBsm103aUQkvKvCBL+KpNtg==", - "dependencies": { - "@babel/runtime": "^7.23.9", - "tslib": "^2.6.2" - } - }, - "node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs": { - "name": "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==", - "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-cjs/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==" - }, - "node_modules/wrap-ansi-cjs/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==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "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==" - }, - "node_modules/ws": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", - "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/xml-js": { - "version": "1.6.11", - "resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz", - "integrity": "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==", - "dev": true, - "dependencies": { - "sax": "^1.2.4" - }, - "bin": { - "xml-js": "bin/cli.js" - } - }, - "node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "engines": { - "node": ">=0.4" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "node_modules/yaml": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz", - "integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==", - "engines": { - "node": ">= 14" - } - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "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==", - "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==" - }, - "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==", - "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" - } - }, - "node_modules/yup": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/yup/-/yup-1.3.3.tgz", - "integrity": "sha512-v8QwZSsHH2K3/G9WSkp6mZKO+hugKT1EmnMqLNUcfu51HU9MDyhlETT/JgtzprnrnQHPWsjc6MUDMBp/l9fNnw==", - "dev": true, - "dependencies": { - "property-expr": "^2.0.5", - "tiny-case": "^1.0.3", - "toposort": "^2.0.2", - "type-fest": "^2.19.0" - } - }, - "node_modules/yup/node_modules/type-fest": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", - "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", - "dev": true, - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/zip-stream": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.1.tgz", - "integrity": "sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==", - "dev": true, - "dependencies": { - "archiver-utils": "^3.0.4", - "compress-commons": "^4.1.2", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/zip-stream/node_modules/archiver-utils": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-3.0.4.tgz", - "integrity": "sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==", - "dev": true, - "dependencies": { - "glob": "^7.2.3", - "graceful-fs": "^4.2.0", - "lazystream": "^1.0.0", - "lodash.defaults": "^4.2.0", - "lodash.difference": "^4.5.0", - "lodash.flatten": "^4.4.0", - "lodash.isplainobject": "^4.0.6", - "lodash.union": "^4.6.0", - "normalize-path": "^3.0.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/zip-stream/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/zip-stream/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/zip-stream/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/zod": { - "version": "3.22.4", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.4.tgz", - "integrity": "sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - }, - "node_modules/zod-to-json-schema": { - "version": "3.22.4", - "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.22.4.tgz", - "integrity": "sha512-2Ed5dJ+n/O3cU383xSY28cuVi0BCQhF8nYqWU5paEpl7fVdqdAmiLdqLyfblbNdfOFwFfi/mqU4O1pwc60iBhQ==", - "peerDependencies": { - "zod": "^3.22.4" - } - }, - "packages/amqpbridge": { - "name": "@purista/amqpbridge", - "version": "1.11.0", - "license": "ISC", - "dependencies": { - "@opentelemetry/api": "^1.7.0", - "@opentelemetry/resources": "^1.19.0", - "@opentelemetry/sdk-trace-node": "^1.19.0", - "@opentelemetry/semantic-conventions": "^1.19.0", - "@purista/core": "*", - "amqplib": "^0.10.3" - }, - "devDependencies": { - "@types/amqplib": "^0.10.4", - "@types/node": "^20.11.17", - "@types/sinon": "^17.0.3", - "sinon": "^17.0.1", - "tshy": "^1.11.1", - "vitest": "^1.3.0" - }, - "engines": { - "node": ">=18.15" - } - }, - "packages/aws-config-store": { - "name": "@purista/aws-config-store", - "version": "1.11.0", - "license": "ISC", - "dependencies": { - "@aws-sdk/client-ssm": "^3.515.0", - "@purista/core": "*" - }, - "devDependencies": { - "@types/node": "^20.11.17", - "@types/sinon": "^17.0.3", - "sinon": "^17.0.1", - "testcontainers": "^10.6.0", - "tshy": "^1.11.1", - "vitest": "^1.3.0" - }, - "engines": { - "node": ">=18.15" - } - }, - "packages/aws-secret-store": { - "name": "@purista/aws-secret-store", - "version": "1.11.0", - "license": "ISC", - "dependencies": { - "@aws-sdk/client-secrets-manager": "^3.515.0", - "@purista/core": "*" - }, - "devDependencies": { - "@types/node": "^20.11.17", - "@types/sinon": "^17.0.3", - "sinon": "^17.0.1", - "testcontainers": "^10.6.0", - "tshy": "^1.11.1", - "vitest": "^1.3.0" - }, - "engines": { - "node": ">=18.15" - } - }, - "packages/azure-secret-store": { - "name": "@purista/azure-secret-store", - "version": "1.11.0", - "license": "ISC", - "dependencies": { - "@azure/identity": "^4.0.1", - "@azure/keyvault-secrets": "^4.8.0", - "@purista/core": "*" - }, - "devDependencies": { - "@types/node": "^20.11.17", - "@types/sinon": "^17.0.3", - "dotenv": "^16.4.4", - "sinon": "^17.0.1", - "testcontainers": "^10.6.0", - "tshy": "^1.11.1", - "vitest": "^1.3.0" - }, - "engines": { - "node": ">=18.15" - } - }, - "packages/base-http-bridge": { - "name": "@purista/base-http-bridge", - "version": "1.11.0", - "license": "ISC", - "dependencies": { - "@opentelemetry/api": "^1.7.0", - "@opentelemetry/semantic-conventions": "^1.19.0", - "@purista/core": "*", - "cloudevents": "^8.0.0", - "hono": "^4.0.4" - }, - "devDependencies": { - "@types/node": "^20.11.17", - "@types/sinon": "^17.0.3", - "sinon": "^17.0.1", - "tshy": "^1.11.1", - "vitest": "^1.3.0" - }, - "engines": { - "node": ">=18.15" - } - }, - "packages/cli": { - "name": "@purista/cli", - "version": "1.11.0", - "license": "ISC", - "dependencies": { - "camelcase": "^8.0.0", - "minimist": "^1.2.8", - "plop": "^4.0.1", - "ts-morph": "^21.0.1", - "zod": "^3.22.4" - }, - "bin": { - "purista": "dist/index.js" - }, - "devDependencies": { - "@types/minimist": "^1.2.5", - "@types/node": "^20.11.17", - "rimraf": "^5.0.5", - "tshy": "^1.11.1", - "tsx": "^4.7.1" - }, - "engines": { - "node": ">=18.15" - } - }, - "packages/core": { - "name": "@purista/core", - "version": "1.11.0", - "license": "ISC", - "dependencies": { - "@opentelemetry/api": "^1.7.0", - "@opentelemetry/resources": "^1.19.0", - "@opentelemetry/sdk-trace-node": "^1.19.0", - "@opentelemetry/semantic-conventions": "^1.19.0", - "@typeschema/main": "^0.13.1", - "@typeschema/zod": "^0.13.1", - "openapi3-ts": "^4.2.1", - "pino": "^8.19.0", - "ts-deepmerge": "^7.0.0", - "zod": "^3.22.4", - "zod-to-json-schema": "^3.22.4" - }, - "devDependencies": { - "@sodaru/yup-to-json-schema": "^2.0.1", - "@types/node": "^20.11.16", - "@typeschema/yup": "^0.13.1", - "sinon": "^17.0.1", - "tshy": "^1.11.1", - "yup": "^1.3.3" - }, - "engines": { - "node": ">=18.15" - }, - "peerDependencies": { - "@types/sinon": "^17.0.3", - "sinon": "^17.x" - }, - "peerDependenciesMeta": { - "@types/sinon": { - "optional": true - }, - "sinon": { - "optional": true - } - } - }, - "packages/core/node_modules/@typeschema/main": { - "version": "0.13.2", - "resolved": "https://registry.npmjs.org/@typeschema/main/-/main-0.13.2.tgz", - "integrity": "sha512-K6DfMxtPL1U+Awkn2k8D48mjVy2Hj3tYHOx9wm2/GdemmIC8ai18MUwoQoSWDqCWLQ3UR2dVuyy8dAeT1NhPcg==", - "dependencies": { - "@typeschema/core": "0.13.1" - }, - "peerDependencies": { - "@typeschema/arktype": "0.13.1", - "@typeschema/deepkit": "0.13.1", - "@typeschema/effect": "0.13.1", - "@typeschema/function": "0.13.1", - "@typeschema/io-ts": "0.13.1", - "@typeschema/joi": "0.13.1", - "@typeschema/json": "0.13.1", - "@typeschema/ow": "0.13.1", - "@typeschema/runtypes": "0.13.1", - "@typeschema/superstruct": "0.13.1", - "@typeschema/typebox": "0.13.1", - "@typeschema/valibot": "0.13.1", - "@typeschema/yup": "0.13.1", - "@typeschema/zod": "0.13.1" - }, - "peerDependenciesMeta": { - "@typeschema/arktype": { - "optional": true - }, - "@typeschema/deepkit": { - "optional": true - }, - "@typeschema/effect": { - "optional": true - }, - "@typeschema/function": { - "optional": true - }, - "@typeschema/io-ts": { - "optional": true - }, - "@typeschema/joi": { - "optional": true - }, - "@typeschema/json": { - "optional": true - }, - "@typeschema/ow": { - "optional": true - }, - "@typeschema/runtypes": { - "optional": true - }, - "@typeschema/superstruct": { - "optional": true - }, - "@typeschema/typebox": { - "optional": true - }, - "@typeschema/valibot": { - "optional": true - }, - "@typeschema/yup": { - "optional": true - }, - "@typeschema/zod": { - "optional": true - } - } - }, - "packages/dapr-sdk": { - "name": "@purista/dapr-sdk", - "version": "1.11.0", - "license": "ISC", - "dependencies": { - "@opentelemetry/api": "^1.7.0", - "@opentelemetry/semantic-conventions": "^1.19.0", - "@purista/base-http-bridge": "*", - "@purista/core": "*" - }, - "devDependencies": { - "@hono/node-server": "^1.8.0", - "@types/node": "^20.11.17", - "@types/sinon": "^17.0.3", - "hono": "^4.0.4", - "sinon": "^17.0.1", - "tshy": "^1.11.1", - "vitest": "^1.3.0" - }, - "engines": { - "node": ">=18.15" - }, - "peerDependencies": { - "@hono/node-server": "^1.4.0" - }, - "peerDependenciesMeta": { - "@hono/node-server": { - "optional": true - } - } - }, - "packages/gcloud-secret-store": { - "name": "@purista/gcloud-secret-store", - "version": "1.11.0", - "license": "ISC", - "dependencies": { - "@google-cloud/secret-manager": "^5.1.0", - "@purista/core": "*" - }, - "devDependencies": { - "@types/node": "^20.11.17", - "@types/sinon": "^17.0.3", - "sinon": "^17.0.1", - "tshy": "^1.11.1", - "vitest": "^1.3.0" - }, - "engines": { - "node": ">=18.15" - } - }, - "packages/hono-http-server": { - "name": "@purista/hono-http-server", - "version": "1.11.0", - "license": "ISC", - "dependencies": { - "@opentelemetry/api": "^1.7.0", - "@opentelemetry/semantic-conventions": "^1.19.0", - "@purista/core": "*", - "hono": "^4.0.4" - }, - "devDependencies": { - "@hono/swagger-ui": "^0.2.1", - "@types/node": "^20.11.17", - "@types/sinon": "^17.0.3", - "sinon": "^17.0.1", - "tshy": "^1.11.1", - "vitest": "^1.3.0" - }, - "engines": { - "node": ">=18.15" - }, - "peerDependencies": { - "@hono/node-server": "^1.8.0", - "@hono/swagger-ui": "^0.2.1" - } - }, - "packages/httpserver": { - "name": "@purista/httpserver", - "version": "1.11.0", - "license": "ISC", - "dependencies": { - "@fastify/compress": "^7.0.0", - "@fastify/cors": "^9.0.0", - "@fastify/helmet": "^11.1.1", - "@fastify/static": "^7.0.1", - "@opentelemetry/api": "^1.7.0", - "@opentelemetry/semantic-conventions": "^1.19.0", - "@purista/core": "*", - "fastify": "^4.26.1", - "openapi3-ts": "4.2.2", - "swagger-ui-dist": "^5.11.7", - "trouter": "^4.0.0" - }, - "devDependencies": { - "@types/node": "^20.11.17", - "@types/qs": "^6.9.11", - "@types/sinon": "^17.0.3", - "@types/swagger-ui-dist": "^3.30.4", - "sinon": "^17.0.1", - "tshy": "^1.11.1", - "vitest": "^1.3.0" - }, - "engines": { - "node": ">=18.15" - } - }, - "packages/infisical-secret-store": { - "name": "@purista/infisical-secret-store", - "version": "1.11.0", - "license": "ISC", - "dependencies": { - "@purista/core": "*" - }, - "devDependencies": { - "@types/node": "^20.11.17", - "@types/sinon": "^17.0.3", - "sinon": "^17.0.1", - "tshy": "^1.11.1", - "vitest": "^1.3.0" - }, - "engines": { - "node": ">=18.15" - } - }, - "packages/k8s-sdk": { - "name": "@purista/k8s-sdk", - "version": "1.11.0", - "license": "ISC", - "dependencies": { - "@opentelemetry/api": "^1.7.0", - "@opentelemetry/semantic-conventions": "^1.19.0", - "@purista/core": "*", - "hono": "^4.0.4" - }, - "devDependencies": { - "@hono/node-server": "^1.8.0", - "@types/node": "^20.11.17", - "@types/sinon": "^17.0.3", - "sinon": "^17.0.1", - "tshy": "^1.11.1", - "vitest": "^1.3.0" - }, - "engines": { - "node": ">=18.15" - }, - "peerDependencies": { - "@hono/node-server": "^1.4.0" - }, - "peerDependenciesMeta": { - "@hono/node-server": { - "optional": true - } - } - }, - "packages/mqttbridge": { - "name": "@purista/mqttbridge", - "version": "1.11.0", - "license": "ISC", - "dependencies": { - "@opentelemetry/api": "^1.7.0", - "@opentelemetry/resources": "^1.19.0", - "@opentelemetry/sdk-trace-node": "^1.19.0", - "@opentelemetry/semantic-conventions": "^1.19.0", - "@purista/core": "*", - "mqtt": "^5.3.5" - }, - "devDependencies": { - "@types/node": "^20.11.17", - "@types/sinon": "^17.0.3", - "@types/ws": "^8.5.10", - "sinon": "^17.0.1", - "tshy": "^1.11.1", - "vitest": "^1.3.0" - }, - "engines": { - "node": ">=18.15" - } - }, - "packages/nats-config-store": { - "name": "@purista/nats-config-store", - "version": "1.11.0", - "license": "ISC", - "dependencies": { - "@purista/core": "*", - "nats": "^2.19.0" - }, - "devDependencies": { - "@testcontainers/nats": "^10.6.0", - "@types/node": "^20.11.17", - "@types/sinon": "^17.0.3", - "sinon": "^17.0.1", - "tshy": "^1.11.1", - "vitest": "^1.3.0" - }, - "engines": { - "node": ">=18.15" - } - }, - "packages/nats-state-store": { - "name": "@purista/nats-state-store", - "version": "1.11.0", - "license": "ISC", - "dependencies": { - "@purista/core": "*", - "nats": "^2.19.0" - }, - "devDependencies": { - "@testcontainers/nats": "^10.6.0", - "@types/node": "^20.11.17", - "@types/sinon": "^17.0.3", - "sinon": "^17.0.1", - "tshy": "^1.11.1", - "vitest": "^1.3.0" - }, - "engines": { - "node": ">=18.15" - } - }, - "packages/natsbridge": { - "name": "@purista/natsbridge", - "version": "1.11.0", - "license": "ISC", - "dependencies": { - "@opentelemetry/api": "^1.7.0", - "@opentelemetry/resources": "^1.19.0", - "@opentelemetry/sdk-trace-node": "^1.19.0", - "@opentelemetry/semantic-conventions": "^1.19.0", - "@purista/core": "*", - "nats": "^2.19.0" - }, - "devDependencies": { - "@testcontainers/nats": "^10.6.0", - "@types/node": "^20.11.17", - "@types/sinon": "^17.0.3", - "@types/ws": "^8.5.10", - "sinon": "^17.0.1", - "tshy": "^1.11.1", - "vitest": "^1.3.0" - }, - "engines": { - "node": ">=18.15" - } - }, - "packages/redis-config-store": { - "name": "@purista/redis-config-store", - "version": "1.11.0", - "license": "ISC", - "dependencies": { - "@purista/core": "*", - "@redis/client": "^1.5.13" - }, - "devDependencies": { - "@types/node": "^20.11.17", - "@types/sinon": "^17.0.3", - "sinon": "^17.0.1", - "tshy": "^1.11.1", - "vitest": "^1.3.0" - }, - "engines": { - "node": ">=18.15" - } - }, - "packages/redis-state-store": { - "name": "@purista/redis-state-store", - "version": "1.11.0", - "license": "ISC", - "dependencies": { - "@purista/core": "*", - "@redis/client": "^1.5.13" - }, - "devDependencies": { - "@types/node": "^20.11.17", - "@types/sinon": "^17.0.3", - "sinon": "^17.0.1", - "tshy": "^1.11.1", - "vitest": "^1.3.0" - }, - "engines": { - "node": ">=18.15" - } - }, - "website": { - "name": "@purista/website", - "version": "1.11.0", - "license": "ISC", - "dependencies": { - "vanilla-cookieconsent": "^3.0.0-rc.17" - }, - "devDependencies": { - "@types/node": "^20.10.5", - "date-fns": "^3.0.6", - "feed": "^4.2.2", - "gray-matter": "^4.0.3", - "typescript": "^5.3.3", - "vitepress": "^1.0.0-rc.32", - "vitepress-sidebar": "^1.18.5", - "vue": "^3.3.13" - } - } - } + "name": "@purista/purista", + "version": "1.11.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "version": "1.11.0", + "workspaces": [ + "./packages/*", + "./examples/*", + "website" + ], + "dependencies": { + "npm": "^11.1.0" + }, + "devDependencies": { + "@biomejs/biome": "^1.9.4", + "@types/node": "^22.5.1", + "@types/sinon": "^17.0.3", + "@vitest/coverage-v8": "^3.0.4", + "@vitest/ui": "^3.0.4", + "git-cliff": "^2.3.0", + "npm-check-updates": "^17.1.0", + "rimraf": "^6.0.1", + "sinon": "^19.0.2", + "testcontainers": "^10.12.0", + "ts-node": "^10.9.2", + "tsx": "^4.19.0", + "typedoc": "^0.27.3", + "typedoc-plugin-markdown": "^4.3.1", + "typescript": "^5.5.4", + "vitest": "^3.0.4" + } + }, + "examples/dapr-example": { + "name": "@purista/dapr-example", + "version": "1.11.0", + "license": "ISC", + "dependencies": { + "@hono/node-server": "^1.12.2", + "@opentelemetry/exporter-zipkin": "^1.25.1", + "@opentelemetry/sdk-trace-node": "^1.26.0", + "@purista/core": "*", + "@purista/dapr-sdk": "*", + "zod": "^3.24.1" + }, + "devDependencies": { + "@types/node": "^22.5.1", + "pino-pretty": "^13.0.0", + "sinon": "^19.0.2", + "tsx": "^4.19.0", + "typescript": "^5.5.4", + "vitest": "^3.0.4" + }, + "engines": { + "node": ">=18.15" + } + }, + "examples/fullexample": { + "name": "@purista/full-example", + "version": "1.11.0", + "license": "ISC", + "dependencies": { + "@fastify/static": "^8.0.3", + "@opentelemetry/exporter-trace-otlp-http": "^0.57.1", + "@opentelemetry/exporter-zipkin": "^1.25.1", + "@opentelemetry/sdk-trace-node": "^1.26.0", + "@purista/amqpbridge": "*", + "@purista/core": "*", + "@purista/httpserver": "*", + "@purista/redis-state-store": "*", + "@uptrace/node": "^1.21.0", + "pino-loki": "^2.3.0", + "pino-pretty": "^13.0.0", + "zod": "^3.24.1" + }, + "devDependencies": { + "@types/node": "^22.5.1", + "sinon": "^19.0.2", + "tsx": "^4.19.0", + "typescript": "^5.5.4", + "vitest": "^3.0.4" + }, + "engines": { + "node": ">=18.15" + } + }, + "examples/hono-example": { + "name": "@purista/hono-example", + "version": "1.11.0", + "license": "ISC", + "dependencies": { + "@hono/node-server": "^1.12.2", + "@purista/core": "*", + "@purista/hono-http-server": "^1.9.0", + "@scalar/hono-api-reference": "^0.5.119", + "zod": "^3.24.1" + }, + "devDependencies": { + "@types/node": "^22.5.1", + "pino-pretty": "^13.0.0", + "sinon": "^19.0.2", + "tsx": "^4.19.0", + "typescript": "^5.5.4", + "vitest": "^3.0.4" + }, + "engines": { + "node": ">=18.15" + } + }, + "examples/kubernetes": { + "name": "@purista/kubernetes-example", + "version": "1.11.0", + "license": "ISC", + "dependencies": { + "@hono/node-server": "^1.12.2", + "@opentelemetry/exporter-trace-otlp-http": "^0.57.1", + "@purista/amqpbridge": "*", + "@purista/core": "*", + "@purista/k8s-sdk": "*", + "zod": "^3.24.1" + }, + "devDependencies": { + "@types/node": "^22.5.1", + "pino-pretty": "^13.0.0", + "sinon": "^19.0.2", + "tsx": "^4.19.0", + "typescript": "^5.5.4", + "vitest": "^3.0.4" + }, + "engines": { + "node": ">=18.15" + } + }, + "examples/mqtt-bridge": { + "name": "@purista/mqtt-example", + "version": "1.11.0", + "license": "ISC", + "dependencies": { + "@fastify/static": "^8.0.3", + "@purista/core": "*", + "@purista/httpserver": "*", + "@purista/mqttbridge": "*", + "zod": "^3.24.1" + }, + "devDependencies": { + "@types/node": "^22.5.1", + "pino-pretty": "^13.0.0", + "sinon": "^19.0.2", + "tsx": "^4.19.0", + "typescript": "^5.5.4", + "vitest": "^3.0.4" + }, + "engines": { + "node": ">=18.15" + } + }, + "examples/nats-bridge": { + "name": "@purista/nats-example", + "version": "1.11.0", + "license": "ISC", + "dependencies": { + "@fastify/static": "^8.0.3", + "@purista/core": "*", + "@purista/httpserver": "*", + "@purista/nats-state-store": "*", + "@purista/natsbridge": "*", + "zod": "^3.24.1" + }, + "devDependencies": { + "@types/node": "^22.5.1", + "pino-pretty": "^13.0.0", + "sinon": "^19.0.2", + "tsx": "^4.19.0", + "typescript": "^5.5.4", + "vitest": "^3.0.4" + }, + "engines": { + "node": ">=18.15" + } + }, + "examples/quickstart": { + "name": "@purista/quickstart", + "version": "1.11.0", + "license": "ISC", + "dependencies": { + "@fastify/static": "^8.0.3", + "@purista/core": "*", + "@purista/httpserver": "*", + "zod": "^3.24.1" + }, + "devDependencies": { + "@types/node": "^22.5.1", + "pino-pretty": "^13.0.0", + "sinon": "^19.0.2", + "tsx": "^4.19.0", + "typescript": "^5.5.4", + "vitest": "^3.0.4" + }, + "engines": { + "node": ">=18.15" + } + }, + "examples/temporal": { + "name": "@purista/temporal-example", + "version": "1.11.0", + "dependencies": { + "@hono/node-server": "^1.12.2", + "@hono/swagger-ui": "^0.5.0", + "@opentelemetry/exporter-trace-otlp-http": "^0.57.1", + "@opentelemetry/sdk-trace-node": "^1.26.0", + "@purista/core": "*", + "@purista/hono-http-server": "*", + "@purista/natsbridge": "*", + "@temporalio/activity": "^1.10.3", + "@temporalio/client": "^1.10.3", + "@temporalio/interceptors-opentelemetry": "^1.10.3", + "@temporalio/worker": "^1.10.3", + "@temporalio/workflow": "^1.10.3", + "zod": "^3.24.1" + }, + "devDependencies": { + "@types/node": "^22.5.1", + "@types/sinon": "^17.0.3", + "pino-pretty": "^13.0.0", + "sinon": "^19.0.2", + "tsx": "^4.19.0", + "typescript": "^5.5.4", + "vitest": "^3.0.4" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@acuminous/bitsyntax": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@acuminous/bitsyntax/-/bitsyntax-0.1.2.tgz", + "integrity": "sha512-29lUK80d1muEQqiUsSo+3A0yP6CdspgC95EnKBMi22Xlwt79i/En4Vr67+cXhU+cZjbti3TgGGC5wy1stIywVQ==", + "license": "MIT", + "dependencies": { + "buffer-more-ints": "~1.0.0", + "debug": "^4.3.4", + "safe-buffer": "~5.1.2" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/@acuminous/bitsyntax/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/@algolia/autocomplete-core": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.17.7.tgz", + "integrity": "sha512-BjiPOW6ks90UKl7TwMv7oNQMnzU+t/wk9mgIDi6b1tXpUek7MW0lbNOUHpvam9pe3lVCf4xPFT+lK7s+e+fs7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/autocomplete-plugin-algolia-insights": "1.17.7", + "@algolia/autocomplete-shared": "1.17.7" + } + }, + "node_modules/@algolia/autocomplete-plugin-algolia-insights": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.17.7.tgz", + "integrity": "sha512-Jca5Ude6yUOuyzjnz57og7Et3aXjbwCSDf/8onLHSQgw1qW3ALl9mrMWaXb5FmPVkV3EtkD2F/+NkT6VHyPu9A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/autocomplete-shared": "1.17.7" + }, + "peerDependencies": { + "search-insights": ">= 1 < 3" + } + }, + "node_modules/@algolia/autocomplete-preset-algolia": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.17.7.tgz", + "integrity": "sha512-ggOQ950+nwbWROq2MOCIL71RE0DdQZsceqrg32UqnhDz8FlO9rL8ONHNsI2R1MH0tkgVIDKI/D0sMiUchsFdWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/autocomplete-shared": "1.17.7" + }, + "peerDependencies": { + "@algolia/client-search": ">= 4.9.1 < 6", + "algoliasearch": ">= 4.9.1 < 6" + } + }, + "node_modules/@algolia/autocomplete-shared": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.17.7.tgz", + "integrity": "sha512-o/1Vurr42U/qskRSuhBH+VKxMvkkUVTLU6WZQr+L5lGZZLYWyhdzWjW0iGXY7EkwRTjBqvN2EsR81yCTGV/kmg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@algolia/client-search": ">= 4.9.1 < 6", + "algoliasearch": ">= 4.9.1 < 6" + } + }, + "node_modules/@algolia/client-abtesting": { + "version": "5.20.1", + "resolved": "https://registry.npmjs.org/@algolia/client-abtesting/-/client-abtesting-5.20.1.tgz", + "integrity": "sha512-73pnrUixMVnfjgldxhRi5eYLraMt95/MhQHevoFtqwy+t2hfayxYBZXJ2k6JJDld8UmjcWwq3wXnvZJCOm7vZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.20.1", + "@algolia/requester-browser-xhr": "5.20.1", + "@algolia/requester-fetch": "5.20.1", + "@algolia/requester-node-http": "5.20.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-analytics": { + "version": "5.20.1", + "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-5.20.1.tgz", + "integrity": "sha512-BRiyL+AwPfGTlo3HbrFDMeTK2z5SaJmB8PBd1JI66d6MeP85+38Mux2FFw+nvDOfBwlGaN/uw2AQTOZ9r4JYtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.20.1", + "@algolia/requester-browser-xhr": "5.20.1", + "@algolia/requester-fetch": "5.20.1", + "@algolia/requester-node-http": "5.20.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-common": { + "version": "5.20.1", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.20.1.tgz", + "integrity": "sha512-Dk4RhklaAbqLzOeJO/MoIFUjcKYGECiAJYYqDzmE/sbXICk5Uo6dGlv8w4z09lmvsASpNUoMvGYHGBK+WkEGpA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-insights": { + "version": "5.20.1", + "resolved": "https://registry.npmjs.org/@algolia/client-insights/-/client-insights-5.20.1.tgz", + "integrity": "sha512-eu5vhmyYgzZjFIPmkoLo/TU4s+IdsjQ+bEfLj2jcMvyfBD4DcqySKp03TrXjdrHPGO2I3fF7dPZOoCgEi1j2/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.20.1", + "@algolia/requester-browser-xhr": "5.20.1", + "@algolia/requester-fetch": "5.20.1", + "@algolia/requester-node-http": "5.20.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-personalization": { + "version": "5.20.1", + "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-5.20.1.tgz", + "integrity": "sha512-TrUCJ0nVqE0PnOGoRG/RCirxWZ6pF+skZgaaESN2IBnJtk/In14xVmoj8Yzck81bGUY/UI+5dUUOOS7YTSVEhQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.20.1", + "@algolia/requester-browser-xhr": "5.20.1", + "@algolia/requester-fetch": "5.20.1", + "@algolia/requester-node-http": "5.20.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-query-suggestions": { + "version": "5.20.1", + "resolved": "https://registry.npmjs.org/@algolia/client-query-suggestions/-/client-query-suggestions-5.20.1.tgz", + "integrity": "sha512-rHHX/30R3Kkx2aZeR7/8+jU0s6h1cNPMAKOvcMUGVmoiuh46F1sxzmiswHLg6CuLrQ0ikhpdhn3ehFSJwHgp2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.20.1", + "@algolia/requester-browser-xhr": "5.20.1", + "@algolia/requester-fetch": "5.20.1", + "@algolia/requester-node-http": "5.20.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-search": { + "version": "5.20.1", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.20.1.tgz", + "integrity": "sha512-YzHD0Nqp7AjvzbFrMIjhCUl6apHkWfZxKDSlMqf80mXkuG52wY289zFlvTfHjHK1nEiDslH3uHYAR/poOOa21Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.20.1", + "@algolia/requester-browser-xhr": "5.20.1", + "@algolia/requester-fetch": "5.20.1", + "@algolia/requester-node-http": "5.20.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/ingestion": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/@algolia/ingestion/-/ingestion-1.20.1.tgz", + "integrity": "sha512-sHNZ8b5tK7TvXMiiKK+89UsXnFthnAZc0vpwvDKygdTqvsfmfJPhthx36eHTAVYfh7NnA1+eqZsT/hMUGeZFkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.20.1", + "@algolia/requester-browser-xhr": "5.20.1", + "@algolia/requester-fetch": "5.20.1", + "@algolia/requester-node-http": "5.20.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/monitoring": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/@algolia/monitoring/-/monitoring-1.20.1.tgz", + "integrity": "sha512-+fHd1U3gSeszCH03UtyUZmprpmcJH6aJKyUTOfY73lKKRR7hVofmV812ahScR0T4xUkBlGjTLeGnsKY0IG6K6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.20.1", + "@algolia/requester-browser-xhr": "5.20.1", + "@algolia/requester-fetch": "5.20.1", + "@algolia/requester-node-http": "5.20.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/recommend": { + "version": "5.20.1", + "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-5.20.1.tgz", + "integrity": "sha512-+IuiUv3OSOFFKoXFMlZHfFzXGqEQbKhncpAcRSAtJmN4pupY4aNblvJ9Wv0SMm7/MSFRy2JLIoYWRSBpSV2yEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.20.1", + "@algolia/requester-browser-xhr": "5.20.1", + "@algolia/requester-fetch": "5.20.1", + "@algolia/requester-node-http": "5.20.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/requester-browser-xhr": { + "version": "5.20.1", + "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.20.1.tgz", + "integrity": "sha512-+RaJa5MpJqPHaSbBw0nrHeyIAd5C4YC9C1LfDbZJqrn5ZwOvHMUTod852XmzX/1S8oi1jTynB4FjicmauZIKwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.20.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/requester-fetch": { + "version": "5.20.1", + "resolved": "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.20.1.tgz", + "integrity": "sha512-4l1cba8t02rNkLeX/B7bmgDg3CwuRunmuCSgN2zORDtepjg9YAU1qcyRTyc/rAuNJ54AduRfoBplmKXjowYzbQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.20.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/requester-node-http": { + "version": "5.20.1", + "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.20.1.tgz", + "integrity": "sha512-4npKo1qpLG5xusFoFUj4xIIR/6y3YoCuS/uhagv2blGFotDj+D6OLTML3Pp6JCVcES4zDbkoY7pmNBA8ARtidQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.20.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", + "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-js": "^5.2.0", + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", + "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-crypto/supports-web-crypto": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", + "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", + "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-secrets-manager": { + "version": "3.744.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-secrets-manager/-/client-secrets-manager-3.744.0.tgz", + "integrity": "sha512-AF5Db4vmy+aWk++d6afxv+DI8YLtWq76AIlFcHv+E9Omb7/Fgzrf9AfevjLTgg8BLK51l+uSdAEfC60K3aD3RA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.744.0", + "@aws-sdk/credential-provider-node": "3.744.0", + "@aws-sdk/middleware-host-header": "3.734.0", + "@aws-sdk/middleware-logger": "3.734.0", + "@aws-sdk/middleware-recursion-detection": "3.734.0", + "@aws-sdk/middleware-user-agent": "3.744.0", + "@aws-sdk/region-config-resolver": "3.734.0", + "@aws-sdk/types": "3.734.0", + "@aws-sdk/util-endpoints": "3.743.0", + "@aws-sdk/util-user-agent-browser": "3.734.0", + "@aws-sdk/util-user-agent-node": "3.744.0", + "@smithy/config-resolver": "^4.0.1", + "@smithy/core": "^3.1.2", + "@smithy/fetch-http-handler": "^5.0.1", + "@smithy/hash-node": "^4.0.1", + "@smithy/invalid-dependency": "^4.0.1", + "@smithy/middleware-content-length": "^4.0.1", + "@smithy/middleware-endpoint": "^4.0.3", + "@smithy/middleware-retry": "^4.0.4", + "@smithy/middleware-serde": "^4.0.2", + "@smithy/middleware-stack": "^4.0.1", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/node-http-handler": "^4.0.2", + "@smithy/protocol-http": "^5.0.1", + "@smithy/smithy-client": "^4.1.3", + "@smithy/types": "^4.1.0", + "@smithy/url-parser": "^4.0.1", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.4", + "@smithy/util-defaults-mode-node": "^4.0.4", + "@smithy/util-endpoints": "^3.0.1", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-retry": "^4.0.1", + "@smithy/util-utf8": "^4.0.0", + "@types/uuid": "^9.0.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-ssm": { + "version": "3.744.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-ssm/-/client-ssm-3.744.0.tgz", + "integrity": "sha512-g39uok7YjyIpMt8d9lZ2HHOR/pRppXLCCesJTCFTIlF7P1Z2Z0VoRYKBa/dNmKS5W3ngc4vsIEXz4ljY/YsUSA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.744.0", + "@aws-sdk/credential-provider-node": "3.744.0", + "@aws-sdk/middleware-host-header": "3.734.0", + "@aws-sdk/middleware-logger": "3.734.0", + "@aws-sdk/middleware-recursion-detection": "3.734.0", + "@aws-sdk/middleware-user-agent": "3.744.0", + "@aws-sdk/region-config-resolver": "3.734.0", + "@aws-sdk/types": "3.734.0", + "@aws-sdk/util-endpoints": "3.743.0", + "@aws-sdk/util-user-agent-browser": "3.734.0", + "@aws-sdk/util-user-agent-node": "3.744.0", + "@smithy/config-resolver": "^4.0.1", + "@smithy/core": "^3.1.2", + "@smithy/fetch-http-handler": "^5.0.1", + "@smithy/hash-node": "^4.0.1", + "@smithy/invalid-dependency": "^4.0.1", + "@smithy/middleware-content-length": "^4.0.1", + "@smithy/middleware-endpoint": "^4.0.3", + "@smithy/middleware-retry": "^4.0.4", + "@smithy/middleware-serde": "^4.0.2", + "@smithy/middleware-stack": "^4.0.1", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/node-http-handler": "^4.0.2", + "@smithy/protocol-http": "^5.0.1", + "@smithy/smithy-client": "^4.1.3", + "@smithy/types": "^4.1.0", + "@smithy/url-parser": "^4.0.1", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.4", + "@smithy/util-defaults-mode-node": "^4.0.4", + "@smithy/util-endpoints": "^3.0.1", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-retry": "^4.0.1", + "@smithy/util-utf8": "^4.0.0", + "@smithy/util-waiter": "^4.0.2", + "@types/uuid": "^9.0.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-sso": { + "version": "3.744.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.744.0.tgz", + "integrity": "sha512-mzJxPQ9mcnNY50pi7+pxB34/Dt7PUn0OgkashHdJPTnavoriLWvPcaQCG1NEVAtyzxNdowhpi4KjC+aN1EwAeA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.744.0", + "@aws-sdk/middleware-host-header": "3.734.0", + "@aws-sdk/middleware-logger": "3.734.0", + "@aws-sdk/middleware-recursion-detection": "3.734.0", + "@aws-sdk/middleware-user-agent": "3.744.0", + "@aws-sdk/region-config-resolver": "3.734.0", + "@aws-sdk/types": "3.734.0", + "@aws-sdk/util-endpoints": "3.743.0", + "@aws-sdk/util-user-agent-browser": "3.734.0", + "@aws-sdk/util-user-agent-node": "3.744.0", + "@smithy/config-resolver": "^4.0.1", + "@smithy/core": "^3.1.2", + "@smithy/fetch-http-handler": "^5.0.1", + "@smithy/hash-node": "^4.0.1", + "@smithy/invalid-dependency": "^4.0.1", + "@smithy/middleware-content-length": "^4.0.1", + "@smithy/middleware-endpoint": "^4.0.3", + "@smithy/middleware-retry": "^4.0.4", + "@smithy/middleware-serde": "^4.0.2", + "@smithy/middleware-stack": "^4.0.1", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/node-http-handler": "^4.0.2", + "@smithy/protocol-http": "^5.0.1", + "@smithy/smithy-client": "^4.1.3", + "@smithy/types": "^4.1.0", + "@smithy/url-parser": "^4.0.1", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.4", + "@smithy/util-defaults-mode-node": "^4.0.4", + "@smithy/util-endpoints": "^3.0.1", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-retry": "^4.0.1", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/core": { + "version": "3.744.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.744.0.tgz", + "integrity": "sha512-R0XLfDDq7MAXYyDf7tPb+m0R7gmzTRRDtPNQ5jvuq8dbkefph5gFMkxZ2zSx7dfTsfYHhBPuTBsQ0c5Xjal3Vg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/core": "^3.1.2", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/property-provider": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/signature-v4": "^5.0.1", + "@smithy/smithy-client": "^4.1.3", + "@smithy/types": "^4.1.0", + "@smithy/util-middleware": "^4.0.1", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.744.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.744.0.tgz", + "integrity": "sha512-hyjC7xqzAeERorYYjhQG1ivcr1XlxgfBpa+r4pG29toFG60mACyVzaR7+og3kgzjRFAB7D1imMxPQyEvQ1QokA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.744.0", + "@aws-sdk/types": "3.734.0", + "@smithy/property-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-http": { + "version": "3.744.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.744.0.tgz", + "integrity": "sha512-k+P1Tl5ewBvVByR6hB726qFIzANgQVf2cY87hZ/e09pQYlH4bfBcyY16VJhkqYnKmv6HMdWxKHX7D8nwlc8Obg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.744.0", + "@aws-sdk/types": "3.734.0", + "@smithy/fetch-http-handler": "^5.0.1", + "@smithy/node-http-handler": "^4.0.2", + "@smithy/property-provider": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/smithy-client": "^4.1.3", + "@smithy/types": "^4.1.0", + "@smithy/util-stream": "^4.0.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.744.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.744.0.tgz", + "integrity": "sha512-hjEWgkF86tkvg8PIsDiB3KkTj7z8ZFGR0v0OLQYD47o17q1qfoMzZmg9wae3wXp9KzU+lZETo+8oMqX9a+7aVQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.744.0", + "@aws-sdk/credential-provider-env": "3.744.0", + "@aws-sdk/credential-provider-http": "3.744.0", + "@aws-sdk/credential-provider-process": "3.744.0", + "@aws-sdk/credential-provider-sso": "3.744.0", + "@aws-sdk/credential-provider-web-identity": "3.744.0", + "@aws-sdk/nested-clients": "3.744.0", + "@aws-sdk/types": "3.734.0", + "@smithy/credential-provider-imds": "^4.0.1", + "@smithy/property-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.744.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.744.0.tgz", + "integrity": "sha512-4oUfRd6pe/VGmKoav17pPoOO0WP0L6YXmHqtJHSDmFUOAa+Vh0ZRljTj/yBdleRgdO6rOfdWqoGLFSFiAZDrsQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.744.0", + "@aws-sdk/credential-provider-http": "3.744.0", + "@aws-sdk/credential-provider-ini": "3.744.0", + "@aws-sdk/credential-provider-process": "3.744.0", + "@aws-sdk/credential-provider-sso": "3.744.0", + "@aws-sdk/credential-provider-web-identity": "3.744.0", + "@aws-sdk/types": "3.734.0", + "@smithy/credential-provider-imds": "^4.0.1", + "@smithy/property-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.744.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.744.0.tgz", + "integrity": "sha512-m0d/pDBIaiEAAxWXt/c79RHsKkUkyPOvF2SAMRddVhhOt1GFZI4ml+3f4drmAZfXldIyJmvJTJJqWluVPwTIqQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.744.0", + "@aws-sdk/types": "3.734.0", + "@smithy/property-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.744.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.744.0.tgz", + "integrity": "sha512-xdMufTZOvpbDoDPI2XLu0/Rg3qJ/txpS8IJR63NsCGotHJZ/ucLNKwTcGS40hllZB8qSHTlvmlOzElDahTtx/A==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.744.0", + "@aws-sdk/core": "3.744.0", + "@aws-sdk/token-providers": "3.744.0", + "@aws-sdk/types": "3.734.0", + "@smithy/property-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.744.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.744.0.tgz", + "integrity": "sha512-cNk93GZxORzqEojWfXdrPBF6a7Nu3LpPCWG5mV+lH2tbuGsmw6XhKkwpt7o+OiIP4tKCpHlvqOD8f1nmhe1KDA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.744.0", + "@aws-sdk/nested-clients": "3.744.0", + "@aws-sdk/types": "3.734.0", + "@smithy/property-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-host-header": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.734.0.tgz", + "integrity": "sha512-LW7RRgSOHHBzWZnigNsDIzu3AiwtjeI2X66v+Wn1P1u+eXssy1+up4ZY/h+t2sU4LU36UvEf+jrZti9c6vRnFw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-logger": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.734.0.tgz", + "integrity": "sha512-mUMFITpJUW3LcKvFok176eI5zXAUomVtahb9IQBwLzkqFYOrMJvWAvoV4yuxrJ8TlQBG8gyEnkb9SnhZvjg67w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.734.0.tgz", + "integrity": "sha512-CUat2d9ITsFc2XsmeiRQO96iWpxSKYFjxvj27Hc7vo87YUHRnfMfnc8jw1EpxEwMcvBD7LsRa6vDNky6AjcrFA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.744.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.744.0.tgz", + "integrity": "sha512-ROUbDQHfVWiBHXd4m9E9mKj1Azby8XCs8RC8OCf9GVH339GSE6aMrPJSzMlsV1LmzPdPIypgp5qqh5NfSrKztg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.744.0", + "@aws-sdk/types": "3.734.0", + "@aws-sdk/util-endpoints": "3.743.0", + "@smithy/core": "^3.1.2", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients": { + "version": "3.744.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.744.0.tgz", + "integrity": "sha512-Mnrlh4lRY1gZQnKvN2Lh/5WXcGkzC41NM93mtn2uaqOh+DZLCXCttNCfbUesUvYJLOo3lYaOpiDsjTkPVB1yjw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.744.0", + "@aws-sdk/middleware-host-header": "3.734.0", + "@aws-sdk/middleware-logger": "3.734.0", + "@aws-sdk/middleware-recursion-detection": "3.734.0", + "@aws-sdk/middleware-user-agent": "3.744.0", + "@aws-sdk/region-config-resolver": "3.734.0", + "@aws-sdk/types": "3.734.0", + "@aws-sdk/util-endpoints": "3.743.0", + "@aws-sdk/util-user-agent-browser": "3.734.0", + "@aws-sdk/util-user-agent-node": "3.744.0", + "@smithy/config-resolver": "^4.0.1", + "@smithy/core": "^3.1.2", + "@smithy/fetch-http-handler": "^5.0.1", + "@smithy/hash-node": "^4.0.1", + "@smithy/invalid-dependency": "^4.0.1", + "@smithy/middleware-content-length": "^4.0.1", + "@smithy/middleware-endpoint": "^4.0.3", + "@smithy/middleware-retry": "^4.0.4", + "@smithy/middleware-serde": "^4.0.2", + "@smithy/middleware-stack": "^4.0.1", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/node-http-handler": "^4.0.2", + "@smithy/protocol-http": "^5.0.1", + "@smithy/smithy-client": "^4.1.3", + "@smithy/types": "^4.1.0", + "@smithy/url-parser": "^4.0.1", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.4", + "@smithy/util-defaults-mode-node": "^4.0.4", + "@smithy/util-endpoints": "^3.0.1", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-retry": "^4.0.1", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/region-config-resolver": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.734.0.tgz", + "integrity": "sha512-Lvj1kPRC5IuJBr9DyJ9T9/plkh+EfKLy+12s/mykOy1JaKHDpvj+XGy2YO6YgYVOb8JFtaqloid+5COtje4JTQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-middleware": "^4.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/token-providers": { + "version": "3.744.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.744.0.tgz", + "integrity": "sha512-v/1+lWkDCd60Ei6oyhJqli6mTsPEVepLoSMB50vHUVlJP0fzXu/3FMje90/RzeUoh/VugZQJCEv/NNpuC6wztg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/nested-clients": "3.744.0", + "@aws-sdk/types": "3.734.0", + "@smithy/property-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/types": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.734.0.tgz", + "integrity": "sha512-o11tSPTT70nAkGV1fN9wm/hAIiLPyWX6SuGf+9JyTp7S/rC2cFWhR26MvA69nplcjNaXVzB0f+QFrLXXjOqCrg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-endpoints": { + "version": "3.743.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.743.0.tgz", + "integrity": "sha512-sN1l559zrixeh5x+pttrnd0A3+r34r0tmPkJ/eaaMaAzXqsmKU/xYre9K3FNnsSS1J1k4PEfk/nHDTVUgFYjnw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/types": "^4.1.0", + "@smithy/util-endpoints": "^3.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-locate-window": { + "version": "3.723.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.723.0.tgz", + "integrity": "sha512-Yf2CS10BqK688DRsrKI/EO6B8ff5J86NXe4C+VCysK7UOgN0l1zOTeTukZ3H8Q9tYYX3oaF1961o8vRkFm7Nmw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.734.0.tgz", + "integrity": "sha512-xQTCus6Q9LwUuALW+S76OL0jcWtMOVu14q+GoLnWPUM7QeUw963oQcLhF7oq0CtaLLKyl4GOUfcwc773Zmwwng==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/types": "^4.1.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.744.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.744.0.tgz", + "integrity": "sha512-BJURjwIXhNa4heXkLC0+GcL+8wVXaU7JoyW6ckdvp93LL+sVHeR1d5FxXZHQW/pMI4E3gNlKyBqjKaT75tObNQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.744.0", + "@aws-sdk/types": "3.734.0", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@azure/abort-controller": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", + "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-auth": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.9.0.tgz", + "integrity": "sha512-FPwHpZywuyasDSLMqJ6fhbOK3TqUdviZNF8OqRGA4W5Ewib2lEEZ+pBsYcBa88B2NGO/SEnYPGhyBqNlE8ilSw==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-util": "^1.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-client": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.9.2.tgz", + "integrity": "sha512-kRdry/rav3fUKHl/aDLd/pDLcB+4pOFwPPTVEExuMyaI5r+JBbMWqRbCY1pn5BniDaU3lRxO9eaQ1AmSMehl/w==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-auth": "^1.4.0", + "@azure/core-rest-pipeline": "^1.9.1", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.6.1", + "@azure/logger": "^1.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-http-compat": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@azure/core-http-compat/-/core-http-compat-2.2.0.tgz", + "integrity": "sha512-1kW8ZhN0CfbNOG6C688z5uh2yrzALE7dDXHiR9dY4vt+EbhGZQSbjDa5bQd2rf3X2pdWMsXbqbArxUyeNdvtmg==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-client": "^1.3.0", + "@azure/core-rest-pipeline": "^1.19.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-lro": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/@azure/core-lro/-/core-lro-2.7.2.tgz", + "integrity": "sha512-0YIpccoX8m/k00O7mDDMdJpbr6mf1yWo2dfmxt5A8XVZVVMz2SSKaEbMCeJRvgQ0IaSlqhjT47p4hVIRRy90xw==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-util": "^1.2.0", + "@azure/logger": "^1.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-paging": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/@azure/core-paging/-/core-paging-1.6.2.tgz", + "integrity": "sha512-YKWi9YuCU04B55h25cnOYZHxXYtEvQEbKST5vqRga7hWY9ydd3FZHdeQF8pyh+acWZvppw13M/LMGx0LABUVMA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-rest-pipeline": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.19.0.tgz", + "integrity": "sha512-bM3308LRyg5g7r3Twprtqww0R/r7+GyVxj4BafcmVPo4WQoGt5JXuaqxHEFjw2o3rvFZcUPiqJMg6WuvEEeVUA==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-auth": "^1.8.0", + "@azure/core-tracing": "^1.0.1", + "@azure/core-util": "^1.11.0", + "@azure/logger": "^1.0.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-tracing": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.2.0.tgz", + "integrity": "sha512-UKTiEJPkWcESPYJz3X5uKRYyOcJD+4nYph+KpfdPRnQJVrZfk0KJgdnaAWKfhsBBtAf/D58Az4AvCJEmWgIBAg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-util": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.11.0.tgz", + "integrity": "sha512-DxOSLua+NdpWoSqULhjDyAZTXFdP/LKkqtYuxxz1SCN289zk3OG8UOpnCQAz/tygyACBtWp/BoO72ptK7msY8g==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/identity": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-4.6.0.tgz", + "integrity": "sha512-ANpO1iAvcZmpD4QY7/kaE/P2n66pRXsDp3nMUC6Ow3c9KfXOZF7qMU9VgqPw8m7adP7TVIbVyrCEmD9cth3KQQ==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-auth": "^1.9.0", + "@azure/core-client": "^1.9.2", + "@azure/core-rest-pipeline": "^1.17.0", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.11.0", + "@azure/logger": "^1.0.0", + "@azure/msal-browser": "^4.0.1", + "@azure/msal-node": "^2.15.0", + "events": "^3.0.0", + "jws": "^4.0.0", + "open": "^8.0.0", + "stoppable": "^1.1.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/keyvault-common": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@azure/keyvault-common/-/keyvault-common-2.0.0.tgz", + "integrity": "sha512-wRLVaroQtOqfg60cxkzUkGKrKMsCP6uYXAOomOIysSMyt1/YM0eUn9LqieAWM8DLcU4+07Fio2YGpPeqUbpP9w==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-auth": "^1.3.0", + "@azure/core-client": "^1.5.0", + "@azure/core-rest-pipeline": "^1.8.0", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.10.0", + "@azure/logger": "^1.1.4", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/keyvault-secrets": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@azure/keyvault-secrets/-/keyvault-secrets-4.9.0.tgz", + "integrity": "sha512-XkLsuzxFdhVIOVcELhso8OhQgY2lSWZcZn6fqjE848FwP9lJemJhu7nxYy8Q1nns6XqPB0WsTPmEkR8eu852vA==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-auth": "^1.3.0", + "@azure/core-client": "^1.5.0", + "@azure/core-http-compat": "^2.0.1", + "@azure/core-lro": "^2.2.0", + "@azure/core-paging": "^1.1.1", + "@azure/core-rest-pipeline": "^1.8.0", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.0.0", + "@azure/keyvault-common": "^2.0.0", + "@azure/logger": "^1.0.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/logger": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.1.4.tgz", + "integrity": "sha512-4IXXzcCdLdlXuCG+8UKEwLA1T1NHqUfanhXYHiQTn+6sfWCZXduqbtXDGceg3Ce5QxTGo7EqmbV6Bi+aqKuClQ==", + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/msal-browser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-4.2.0.tgz", + "integrity": "sha512-MXQjgAgjg/2VRKV+UPWHESoZPcue2ZvWKfpBLCyTUyixP+mhCl0q5D1+xDiwBGV3lru2poKZVZDQAOE40wKmWg==", + "license": "MIT", + "dependencies": { + "@azure/msal-common": "15.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@azure/msal-common": { + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-15.1.1.tgz", + "integrity": "sha512-bvLWYq9fleAcTJ6H+hfkG91On6vI/UhGyOB7Z6r0Bsa+KTL3zPtigmGCOJgdxrEklOYD88X9SehexLDH/5NRKQ==", + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@azure/msal-node": { + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-2.16.2.tgz", + "integrity": "sha512-An7l1hEr0w1HMMh1LU+rtDtqL7/jw74ORlc9Wnh06v7TU/xpG39/Zdr1ZJu3QpjUfKJ+E0/OXMW8DRSWTlh7qQ==", + "license": "MIT", + "dependencies": { + "@azure/msal-common": "14.16.0", + "jsonwebtoken": "^9.0.0", + "uuid": "^8.3.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@azure/msal-node/node_modules/@azure/msal-common": { + "version": "14.16.0", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-14.16.0.tgz", + "integrity": "sha512-1KOZj9IpcDSwpNiQNjt0jDYZpQvNZay7QAEi/5DLubay40iGYtLzya/jbjRPLyOTZhEKyL1MzPuw2HqBCjceYA==", + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@azure/msal-node/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.8.tgz", + "integrity": "sha512-TZIQ25pkSoaKEYYaHbbxkfL36GNsQ6iFiBbeuzAkLnXayKR1yP1zFe+NxuZWWsUyvt8icPU9CCq0sgWGXR1GEw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.26.8" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.7.tgz", + "integrity": "sha512-AOPI3D+a8dXnja+iwsUqGRjr1BbZIe771sXdapOtYI531gSqpi92vXivKcq2asu/DFpdl1ceFAKZyRzK2PCVcQ==", + "license": "MIT", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.8.tgz", + "integrity": "sha512-eUuWapzEGWFEpHFxgEaBG8e3n6S8L3MSu0oda755rOfabWPnh0Our1AozNFVUxGFIhbKgd1ksprsoDGMinTOTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@balena/dockerignore": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@balena/dockerignore/-/dockerignore-1.0.2.tgz", + "integrity": "sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@bcoe/v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz", + "integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@biomejs/biome": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-1.9.4.tgz", + "integrity": "sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog==", + "dev": true, + "hasInstallScript": true, + "license": "MIT OR Apache-2.0", + "bin": { + "biome": "bin/biome" + }, + "engines": { + "node": ">=14.21.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/biome" + }, + "optionalDependencies": { + "@biomejs/cli-darwin-arm64": "1.9.4", + "@biomejs/cli-darwin-x64": "1.9.4", + "@biomejs/cli-linux-arm64": "1.9.4", + "@biomejs/cli-linux-arm64-musl": "1.9.4", + "@biomejs/cli-linux-x64": "1.9.4", + "@biomejs/cli-linux-x64-musl": "1.9.4", + "@biomejs/cli-win32-arm64": "1.9.4", + "@biomejs/cli-win32-x64": "1.9.4" + } + }, + "node_modules/@biomejs/cli-darwin-arm64": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-1.9.4.tgz", + "integrity": "sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-darwin-x64": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-1.9.4.tgz", + "integrity": "sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-arm64": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-1.9.4.tgz", + "integrity": "sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-arm64-musl": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.9.4.tgz", + "integrity": "sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-x64": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-1.9.4.tgz", + "integrity": "sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-x64-musl": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-1.9.4.tgz", + "integrity": "sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-win32-arm64": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-1.9.4.tgz", + "integrity": "sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-win32-x64": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-1.9.4.tgz", + "integrity": "sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "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, + "license": "MIT", + "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, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@docsearch/css": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.8.2.tgz", + "integrity": "sha512-y05ayQFyUmCXze79+56v/4HpycYF3uFqB78pLPrSV5ZKAlDuIAAJNhaRi8tTdRNXh05yxX/TyNnzD6LwSM89vQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@docsearch/js": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/@docsearch/js/-/js-3.8.2.tgz", + "integrity": "sha512-Q5wY66qHn0SwA7Taa0aDbHiJvaFJLOJyHmooQ7y8hlwwQLQ/5WwCcoX0g7ii04Qi2DJlHsd0XXzJ8Ypw9+9YmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@docsearch/react": "3.8.2", + "preact": "^10.0.0" + } + }, + "node_modules/@docsearch/react": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.8.2.tgz", + "integrity": "sha512-xCRrJQlTt8N9GU0DG4ptwHRkfnSnD/YpdeaXe02iKfqs97TkZJv60yE+1eq/tjPcVnTW8dP5qLP7itifFVV5eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/autocomplete-core": "1.17.7", + "@algolia/autocomplete-preset-algolia": "1.17.7", + "@docsearch/css": "3.8.2", + "algoliasearch": "^5.14.2" + }, + "peerDependencies": { + "@types/react": ">= 16.8.0 < 19.0.0", + "react": ">= 16.8.0 < 19.0.0", + "react-dom": ">= 16.8.0 < 19.0.0", + "search-insights": ">= 1 < 3" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + }, + "search-insights": { + "optional": true + } + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz", + "integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz", + "integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz", + "integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz", + "integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz", + "integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz", + "integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz", + "integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz", + "integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz", + "integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz", + "integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz", + "integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz", + "integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz", + "integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz", + "integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz", + "integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz", + "integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz", + "integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.24.2.tgz", + "integrity": "sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz", + "integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz", + "integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz", + "integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz", + "integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz", + "integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz", + "integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz", + "integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@fastify/accept-negotiator": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@fastify/accept-negotiator/-/accept-negotiator-2.0.1.tgz", + "integrity": "sha512-/c/TW2bO/v9JeEgoD/g1G5GxGeCF1Hafdf79WPmUlgYiBXummY0oX3VVq4yFkKKVBKDNlaDUYoab7g38RpPqCQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" + }, + "node_modules/@fastify/ajv-compiler": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@fastify/ajv-compiler/-/ajv-compiler-4.0.2.tgz", + "integrity": "sha512-Rkiu/8wIjpsf46Rr+Fitd3HRP+VsxUFDDeag0hs9L0ksfnwx2g7SPQQTFL0E8Qv+rfXzQOxBJnjUB9ITUDjfWQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "ajv": "^8.12.0", + "ajv-formats": "^3.0.1", + "fast-uri": "^3.0.0" + } + }, + "node_modules/@fastify/ajv-compiler/node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/@fastify/busboy": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", + "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/@fastify/compress": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/@fastify/compress/-/compress-8.0.1.tgz", + "integrity": "sha512-yWNfKhvL4orfN45LKCHCo8Fcsbj1kdNgwyShw2xpdHfzPf4A3MESmgSfUm3TCKQwgqDdrPnLfy1E+3I/DVP+BQ==", + "license": "MIT", + "dependencies": { + "@fastify/accept-negotiator": "^2.0.0", + "fastify-plugin": "^5.0.0", + "mime-db": "^1.52.0", + "minipass": "^7.0.4", + "peek-stream": "^1.1.3", + "pump": "^3.0.0", + "pumpify": "^2.0.1", + "readable-stream": "^4.5.2" + } + }, + "node_modules/@fastify/cors": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@fastify/cors/-/cors-10.0.2.tgz", + "integrity": "sha512-DGdxOG36sS/tZv1NFiCJGi7wGuXOSPL2CmNX5PbOVKx0C6LuIALRMrqLByHTCcX1Rbl8NJ9IWlJex32bzydvlw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "fastify-plugin": "^5.0.0", + "mnemonist": "0.39.8" + } + }, + "node_modules/@fastify/error": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@fastify/error/-/error-4.0.0.tgz", + "integrity": "sha512-OO/SA8As24JtT1usTUTKgGH7uLvhfwZPwlptRi2Dp5P4KKmJI3gvsZ8MIHnNwDs4sLf/aai5LzTyl66xr7qMxA==", + "license": "MIT" + }, + "node_modules/@fastify/fast-json-stringify-compiler": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@fastify/fast-json-stringify-compiler/-/fast-json-stringify-compiler-5.0.2.tgz", + "integrity": "sha512-YdR7gqlLg1xZAQa+SX4sMNzQHY5pC54fu9oC5aYSUqBhyn6fkLkrdtKlpVdCNPlwuUuXA1PjFTEmvMF6ZVXVGw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "fast-json-stringify": "^6.0.0" + } + }, + "node_modules/@fastify/forwarded": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@fastify/forwarded/-/forwarded-3.0.0.tgz", + "integrity": "sha512-kJExsp4JCms7ipzg7SJ3y8DwmePaELHxKYtg+tZow+k0znUTf3cb+npgyqm8+ATZOdmfgfydIebPDWM172wfyA==", + "license": "MIT" + }, + "node_modules/@fastify/helmet": { + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/@fastify/helmet/-/helmet-13.0.1.tgz", + "integrity": "sha512-i+ifqazG3d0HwHL3zuZdg6B/WPc9Ee6kVfGpwGho4nxm0UaK1htss0zq+1rVhOoAorZlCgTZ3/i4S58hUGkkoA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "fastify-plugin": "^5.0.0", + "helmet": "^8.0.0" + } + }, + "node_modules/@fastify/merge-json-schemas": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@fastify/merge-json-schemas/-/merge-json-schemas-0.2.1.tgz", + "integrity": "sha512-OA3KGBCy6KtIvLf8DINC5880o5iBlDX4SxzLQS8HorJAbqluzLRn80UXU0bxZn7UOFhFgpRJDasfwn9nG4FG4A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "dequal": "^2.0.3" + } + }, + "node_modules/@fastify/proxy-addr": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@fastify/proxy-addr/-/proxy-addr-5.0.0.tgz", + "integrity": "sha512-37qVVA1qZ5sgH7KpHkkC4z9SK6StIsIcOmpjvMPXNb3vx2GQxhZocogVYbr2PbbeLCQxYIPDok307xEvRZOzGA==", + "license": "MIT", + "dependencies": { + "@fastify/forwarded": "^3.0.0", + "ipaddr.js": "^2.1.0" + } + }, + "node_modules/@fastify/send": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@fastify/send/-/send-3.3.1.tgz", + "integrity": "sha512-6pofeVwaHN+E/MAofCwDqkWUliE3i++jlD0VH/LOfU8TJlCkMUSgKvA9bawDdVXxjve7XrdYMyDmkiYaoGWEtA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "@lukeed/ms": "^2.0.2", + "escape-html": "~1.0.3", + "fast-decode-uri-component": "^1.0.1", + "http-errors": "^2.0.0", + "mime": "^3" + } + }, + "node_modules/@fastify/static": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@fastify/static/-/static-8.1.0.tgz", + "integrity": "sha512-lPb8+1ulvbGSUSQ0/adBDyp/Ye/MX+pBwhkLAr8/GU88kNnJlSu7KXdyW6CCOROcr5BgrqJD01lEOosozFAegw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "@fastify/accept-negotiator": "^2.0.0", + "@fastify/send": "^3.2.0", + "content-disposition": "^0.5.4", + "fastify-plugin": "^5.0.0", + "fastq": "^1.17.1", + "glob": "^11.0.0" + } + }, + "node_modules/@gerrit0/mini-shiki": { + "version": "1.27.2", + "resolved": "https://registry.npmjs.org/@gerrit0/mini-shiki/-/mini-shiki-1.27.2.tgz", + "integrity": "sha512-GeWyHz8ao2gBiUW4OJnQDxXQnFgZQwwQk05t/CVVgNBN7/rK8XZ7xY6YhLVv9tH3VppWWmr9DCl3MwemB/i+Og==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/engine-oniguruma": "^1.27.2", + "@shikijs/types": "^1.27.2", + "@shikijs/vscode-textmate": "^10.0.1" + } + }, + "node_modules/@google-cloud/secret-manager": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@google-cloud/secret-manager/-/secret-manager-5.6.0.tgz", + "integrity": "sha512-0daW/OXQEVc6VQKPyJTQNyD+563I/TYQ7GCQJx4dq3lB666R9FUPvqHx9b/o/qQtZ5pfuoCbGZl3krpxgTSW8Q==", + "license": "Apache-2.0", + "dependencies": { + "google-gax": "^4.0.3" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@grpc/grpc-js": { + "version": "1.12.6", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.12.6.tgz", + "integrity": "sha512-JXUj6PI0oqqzTGvKtzOkxtpsyPRNsrmhh41TtIz/zEB6J+AUiZZ0dxWzcMwO9Ns5rmSPuMdghlTbUuqIM48d3Q==", + "license": "Apache-2.0", + "dependencies": { + "@grpc/proto-loader": "^0.7.13", + "@js-sdsl/ordered-map": "^4.4.2" + }, + "engines": { + "node": ">=12.10.0" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.7.13", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.13.tgz", + "integrity": "sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==", + "license": "Apache-2.0", + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.2.5", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@hono/node-server": { + "version": "1.13.8", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.13.8.tgz", + "integrity": "sha512-fsn8ucecsAXUoVxrUil0m13kOEq4mkX4/4QozCqmY+HpGfKl74OYSn8JcMA8GnG0ClfdRI4/ZSeG7zhFaVg+wg==", + "license": "MIT", + "engines": { + "node": ">=18.14.1" + }, + "peerDependencies": { + "hono": "^4" + } + }, + "node_modules/@hono/swagger-ui": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@hono/swagger-ui/-/swagger-ui-0.5.0.tgz", + "integrity": "sha512-MWYYSv9kC8IwFBLZdwgZZMT9zUq2C/4/ekuyEYOkHEgUMqu+FG3eebtBZ4ofMh60xYRxRR2BgQGoNIILys/PFg==", + "license": "MIT", + "peerDependencies": { + "hono": "*" + } + }, + "node_modules/@iconify-json/simple-icons": { + "version": "1.2.24", + "resolved": "https://registry.npmjs.org/@iconify-json/simple-icons/-/simple-icons-1.2.24.tgz", + "integrity": "sha512-06ZWXZx3PHCE+02zn+iIGOKKNgE3kyPd0Yh7IUEIa0bCYI6UmGlsYYghRx8As9TnTNYMCEiy5V0zI4Jb6EY6XA==", + "dev": true, + "license": "CC0-1.0", + "dependencies": { + "@iconify/types": "*" + } + }, + "node_modules/@iconify/types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", + "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@inquirer/figures": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.10.tgz", + "integrity": "sha512-Ey6176gZmeqZuY/W/nZiUyvmb1/qInjcpiZjXWi6nON+nxJpD1bxtSoBxNliGISae32n6OwbY+TSXPZ1CfS4bw==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "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, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "license": "MIT", + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@js-sdsl/ordered-map": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", + "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, + "node_modules/@jsonjoy.com/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==", + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/json-pack": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-1.1.1.tgz", + "integrity": "sha512-osjeBqMJ2lb/j/M8NCPjs1ylqWIcTRTycIhVB5pt6LgzgeRSb0YRZ7j9RfA8wIUrsr/medIuhVyonXRZWLyfdw==", + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/base64": "^1.1.1", + "@jsonjoy.com/util": "^1.1.2", + "hyperdyperid": "^1.2.0", + "thingies": "^1.20.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/util": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/util/-/util-1.5.0.tgz", + "integrity": "sha512-ojoNsrIuPI9g6o8UxhraZQSyF2ByJanAY4cTFbc8Mf2AXEF4aQRGY1dJxyJpuyav8r9FGflEt/Ff3u5Nt6YMPA==", + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@lukeed/ms": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@lukeed/ms/-/ms-2.0.2.tgz", + "integrity": "sha512-9I2Zn6+NJLfaGoz9jN3lpwDgAYvfGeNYdbAIjJOqzs4Tpc+VU3Jqq4IofSUBKajiDS8k9fZIg18/z13mpk1bsA==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@opentelemetry/api": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", + "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", + "license": "Apache-2.0", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/api-logs": { + "version": "0.57.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.57.1.tgz", + "integrity": "sha512-I4PHczeujhQAQv6ZBzqHYEUiggZL4IdSMixtVD3EYqbdrjujE7kRfI5QohjlPoJm8BvenoW5YaTMWRrbpot6tg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/auto-instrumentations-node/-/auto-instrumentations-node-0.52.1.tgz", + "integrity": "sha512-4QaRTZifSoYnh27B3JA7z7YwE0Nwkd824pDeonAQVijeLLsenhZB1japualZ6mF9lY8VdQId9KkNsgmCGdJVNQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.54.0", + "@opentelemetry/instrumentation-amqplib": "^0.43.0", + "@opentelemetry/instrumentation-aws-lambda": "^0.47.0", + "@opentelemetry/instrumentation-aws-sdk": "^0.46.0", + "@opentelemetry/instrumentation-bunyan": "^0.42.0", + "@opentelemetry/instrumentation-cassandra-driver": "^0.42.0", + "@opentelemetry/instrumentation-connect": "^0.40.0", + "@opentelemetry/instrumentation-cucumber": "^0.10.0", + "@opentelemetry/instrumentation-dataloader": "^0.13.0", + "@opentelemetry/instrumentation-dns": "^0.40.0", + "@opentelemetry/instrumentation-express": "^0.44.0", + "@opentelemetry/instrumentation-fastify": "^0.41.0", + "@opentelemetry/instrumentation-fs": "^0.16.0", + "@opentelemetry/instrumentation-generic-pool": "^0.40.0", + "@opentelemetry/instrumentation-graphql": "^0.44.0", + "@opentelemetry/instrumentation-grpc": "^0.54.0", + "@opentelemetry/instrumentation-hapi": "^0.42.0", + "@opentelemetry/instrumentation-http": "^0.54.0", + "@opentelemetry/instrumentation-ioredis": "^0.44.0", + "@opentelemetry/instrumentation-kafkajs": "^0.4.0", + "@opentelemetry/instrumentation-knex": "^0.41.0", + "@opentelemetry/instrumentation-koa": "^0.44.0", + "@opentelemetry/instrumentation-lru-memoizer": "^0.41.0", + "@opentelemetry/instrumentation-memcached": "^0.40.0", + "@opentelemetry/instrumentation-mongodb": "^0.48.0", + "@opentelemetry/instrumentation-mongoose": "^0.43.0", + "@opentelemetry/instrumentation-mysql": "^0.42.0", + "@opentelemetry/instrumentation-mysql2": "^0.42.1", + "@opentelemetry/instrumentation-nestjs-core": "^0.41.0", + "@opentelemetry/instrumentation-net": "^0.40.0", + "@opentelemetry/instrumentation-pg": "^0.47.1", + "@opentelemetry/instrumentation-pino": "^0.43.0", + "@opentelemetry/instrumentation-redis": "^0.43.0", + "@opentelemetry/instrumentation-redis-4": "^0.43.0", + "@opentelemetry/instrumentation-restify": "^0.42.0", + "@opentelemetry/instrumentation-router": "^0.41.0", + "@opentelemetry/instrumentation-socket.io": "^0.43.0", + "@opentelemetry/instrumentation-tedious": "^0.15.0", + "@opentelemetry/instrumentation-undici": "^0.7.1", + "@opentelemetry/instrumentation-winston": "^0.41.0", + "@opentelemetry/resource-detector-alibaba-cloud": "^0.29.4", + "@opentelemetry/resource-detector-aws": "^1.7.0", + "@opentelemetry/resource-detector-azure": "^0.2.12", + "@opentelemetry/resource-detector-container": "^0.5.0", + "@opentelemetry/resource-detector-gcp": "^0.29.13", + "@opentelemetry/resources": "^1.24.0", + "@opentelemetry/sdk-node": "^0.54.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.4.1" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/api-logs": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.54.2.tgz", + "integrity": "sha512-4MTVwwmLgUh5QrJnZpYo6YRO5IBLAggf2h8gWDblwRagDStY13aEvt7gGk3jewrMaPlHiF83fENhIx0HO97/cQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/context-async-hooks": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.27.0.tgz", + "integrity": "sha512-CdZ3qmHCwNhFAzjTgHqrDQ44Qxcpz43cVxZRhOs+Ns/79ug+Mr84Bkb626bkJLkA3+BLimA5YAEVRlJC6pFb7g==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/core": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.27.0.tgz", + "integrity": "sha512-yQPKnK5e+76XuiqUH/gKyS8wv/7qITd5ln56QkBTf3uggr0VkXOXfcaAuG330UfdYu83wsyoBwqwxigpIG+Jkg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/exporter-logs-otlp-grpc": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-grpc/-/exporter-logs-otlp-grpc-0.54.2.tgz", + "integrity": "sha512-MQNmV5r96+5n3axLFgNYtVy62x8Ru7VERZH3zgC50KDcIKWCiQT3vHOtzakhzd1Wq0HqOgu6bzKdwzneSoDrEQ==", + "license": "Apache-2.0", + "dependencies": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "1.27.0", + "@opentelemetry/otlp-grpc-exporter-base": "0.54.2", + "@opentelemetry/otlp-transformer": "0.54.2", + "@opentelemetry/sdk-logs": "0.54.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/exporter-logs-otlp-http": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-http/-/exporter-logs-otlp-http-0.54.2.tgz", + "integrity": "sha512-wYeCSbX2XWX2wFslnfQ/YFUolO0fj2nUiGI7oEQWpLKSg40Lc4xOOW14X/EXOkCCijhP7bigo6nvyEQlxEVLjA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.54.2", + "@opentelemetry/core": "1.27.0", + "@opentelemetry/otlp-exporter-base": "0.54.2", + "@opentelemetry/otlp-transformer": "0.54.2", + "@opentelemetry/sdk-logs": "0.54.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/exporter-logs-otlp-proto": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-proto/-/exporter-logs-otlp-proto-0.54.2.tgz", + "integrity": "sha512-agrzFbSNmIy6dhkyg41ERlEDUDqkaUJj2n/tVRFp9Tl+6wyNVPsqmwU5RWJOXpyK+lYH/znv6A47VpTeJF0lrw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.54.2", + "@opentelemetry/core": "1.27.0", + "@opentelemetry/otlp-exporter-base": "0.54.2", + "@opentelemetry/otlp-transformer": "0.54.2", + "@opentelemetry/resources": "1.27.0", + "@opentelemetry/sdk-logs": "0.54.2", + "@opentelemetry/sdk-trace-base": "1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/exporter-trace-otlp-grpc": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-grpc/-/exporter-trace-otlp-grpc-0.54.2.tgz", + "integrity": "sha512-tmxiCYhQdPrzwlM6O7VQeNP9PBjKhaiOo54wFxQFZQcoVaDiOOES4+6PwHU1eW+43mDsgdQHN5AHSRHVLe9jDA==", + "license": "Apache-2.0", + "dependencies": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "1.27.0", + "@opentelemetry/otlp-grpc-exporter-base": "0.54.2", + "@opentelemetry/otlp-transformer": "0.54.2", + "@opentelemetry/resources": "1.27.0", + "@opentelemetry/sdk-trace-base": "1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/exporter-trace-otlp-http": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.54.2.tgz", + "integrity": "sha512-BgWKKyD/h2zpISdmYHN/sapwTjvt1P4p5yx4xeBV8XAEqh4OQUhOtSGFG80+nPQ1F8of3mKOT1DDoDbJp1u25w==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.27.0", + "@opentelemetry/otlp-exporter-base": "0.54.2", + "@opentelemetry/otlp-transformer": "0.54.2", + "@opentelemetry/resources": "1.27.0", + "@opentelemetry/sdk-trace-base": "1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/exporter-trace-otlp-proto": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-proto/-/exporter-trace-otlp-proto-0.54.2.tgz", + "integrity": "sha512-XSmm1N2wAhoWDXP1q/N6kpLebWaxl6VIADv4WA5QWKHLRpF3gLz5NAWNJBR8ygsvv8jQcrwnXgwfnJ18H3v1fg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.27.0", + "@opentelemetry/otlp-exporter-base": "0.54.2", + "@opentelemetry/otlp-transformer": "0.54.2", + "@opentelemetry/resources": "1.27.0", + "@opentelemetry/sdk-trace-base": "1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/exporter-zipkin": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-zipkin/-/exporter-zipkin-1.27.0.tgz", + "integrity": "sha512-eGMY3s4QprspFZojqsuQyQpWNFpo+oNVE/aosTbtvAlrJBAlvXcwwsOROOHOd8Y9lkU4i0FpQW482rcXkgwCSw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.27.0", + "@opentelemetry/resources": "1.27.0", + "@opentelemetry/sdk-trace-base": "1.27.0", + "@opentelemetry/semantic-conventions": "1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/instrumentation": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.54.2.tgz", + "integrity": "sha512-go6zpOVoZVztT9r1aPd79Fr3OWiD4N24bCPJsIKkBses8oyFo12F/Ew3UBTdIu6hsW4HC4MVEJygG6TEyJI/lg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.54.2", + "@types/shimmer": "^1.2.0", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/instrumentation-amqplib": { + "version": "0.43.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-amqplib/-/instrumentation-amqplib-0.43.0.tgz", + "integrity": "sha512-ALjfQC+0dnIEcvNYsbZl/VLh7D2P1HhFF4vicRKHhHFIUV3Shpg4kXgiek5PLhmeKSIPiUB25IYH5RIneclL4A==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.54.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/otlp-exporter-base": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.54.2.tgz", + "integrity": "sha512-NrNyxu6R/bGAwanhz1HI0aJWKR6xUED4TjCH4iWMlAfyRukGbI9Kt/Akd2sYLwRKNhfS+sKetKGCUQPMDyYYMA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.27.0", + "@opentelemetry/otlp-transformer": "0.54.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/otlp-grpc-exporter-base": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-grpc-exporter-base/-/otlp-grpc-exporter-base-0.54.2.tgz", + "integrity": "sha512-HZtACQuLhgDcgNa9arGnVVGV28sSGQ+iwRgICWikFKiVxUsoWffqBvTxPa6G3DUTg5R+up97j/zxubEyxSAOHg==", + "license": "Apache-2.0", + "dependencies": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "1.27.0", + "@opentelemetry/otlp-exporter-base": "0.54.2", + "@opentelemetry/otlp-transformer": "0.54.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/otlp-transformer": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.54.2.tgz", + "integrity": "sha512-2tIjahJlMRRUz0A2SeE+qBkeBXBFkSjR0wqJ08kuOqaL8HNGan5iZf+A8cfrfmZzPUuMKCyY9I+okzFuFs6gKQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.54.2", + "@opentelemetry/core": "1.27.0", + "@opentelemetry/resources": "1.27.0", + "@opentelemetry/sdk-logs": "0.54.2", + "@opentelemetry/sdk-metrics": "1.27.0", + "@opentelemetry/sdk-trace-base": "1.27.0", + "protobufjs": "^7.3.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/propagator-b3": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-1.27.0.tgz", + "integrity": "sha512-pTsko3gnMioe3FeWcwTQR3omo5C35tYsKKwjgTCTVCgd3EOWL9BZrMfgLBmszrwXABDfUrlAEFN/0W0FfQGynQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/propagator-jaeger": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-1.27.0.tgz", + "integrity": "sha512-EI1bbK0wn0yIuKlc2Qv2LKBRw6LiUWevrjCF80fn/rlaB+7StAi8Y5s8DBqAYNpY7v1q86+NjU18v7hj2ejU3A==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/resources": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.27.0.tgz", + "integrity": "sha512-jOwt2VJ/lUD5BLc+PMNymDrUCpm5PKi1E9oSVYAvz01U/VdndGmrtV3DU1pG4AwlYhJRHbHfOUIlpBeXCPw6QQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.27.0", + "@opentelemetry/semantic-conventions": "1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/sdk-logs": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.54.2.tgz", + "integrity": "sha512-yIbYqDLS/AtBbPjCjh6eSToGNRMqW2VR8RrKEy+G+J7dFG7pKoptTH5T+XlKPleP9NY8JZYIpgJBlI+Osi0rFw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.54.2", + "@opentelemetry/core": "1.27.0", + "@opentelemetry/resources": "1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.4.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/sdk-metrics": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.27.0.tgz", + "integrity": "sha512-JzWgzlutoXCydhHWIbLg+r76m+m3ncqvkCcsswXAQ4gqKS+LOHKhq+t6fx1zNytvLuaOUBur7EvWxECc4jPQKg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.27.0", + "@opentelemetry/resources": "1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/sdk-node": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-node/-/sdk-node-0.54.2.tgz", + "integrity": "sha512-afn8GBpA7Gb55aU0LUxIQ+oe6QxLhsf+Te9iw12Non3ZAspzdoCcfz5+hqecwpuVpEDdnj5iSalF7VVaL2pDeg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.54.2", + "@opentelemetry/core": "1.27.0", + "@opentelemetry/exporter-logs-otlp-grpc": "0.54.2", + "@opentelemetry/exporter-logs-otlp-http": "0.54.2", + "@opentelemetry/exporter-logs-otlp-proto": "0.54.2", + "@opentelemetry/exporter-trace-otlp-grpc": "0.54.2", + "@opentelemetry/exporter-trace-otlp-http": "0.54.2", + "@opentelemetry/exporter-trace-otlp-proto": "0.54.2", + "@opentelemetry/exporter-zipkin": "1.27.0", + "@opentelemetry/instrumentation": "0.54.2", + "@opentelemetry/resources": "1.27.0", + "@opentelemetry/sdk-logs": "0.54.2", + "@opentelemetry/sdk-metrics": "1.27.0", + "@opentelemetry/sdk-trace-base": "1.27.0", + "@opentelemetry/sdk-trace-node": "1.27.0", + "@opentelemetry/semantic-conventions": "1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.27.0.tgz", + "integrity": "sha512-btz6XTQzwsyJjombpeqCX6LhiMQYpzt2pIYNPnw0IPO/3AhT6yjnf8Mnv3ZC2A4eRYOjqrg+bfaXg9XHDRJDWQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.27.0", + "@opentelemetry/resources": "1.27.0", + "@opentelemetry/semantic-conventions": "1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/sdk-trace-node": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-1.27.0.tgz", + "integrity": "sha512-dWZp/dVGdUEfRBjBq2BgNuBlFqHCxyyMc8FsN0NX15X07mxSUO0SZRLyK/fdAVrde8nqFI/FEdMH4rgU9fqJfQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/context-async-hooks": "1.27.0", + "@opentelemetry/core": "1.27.0", + "@opentelemetry/propagator-b3": "1.27.0", + "@opentelemetry/propagator-jaeger": "1.27.0", + "@opentelemetry/sdk-trace-base": "1.27.0", + "semver": "^7.5.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.27.0.tgz", + "integrity": "sha512-sAay1RrB+ONOem0OZanAR1ZI/k7yDpnOQSQmTMuGImUQb2y8EbSaCJ94FQluM74xoU03vlb2d2U90hZluL6nQg==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/context-async-hooks": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.30.1.tgz", + "integrity": "sha512-s5vvxXPVdjqS3kTLKMeBMvop9hbWkwzBpu+mUO2M7sZtlkyDJGwFe33wRKnbaYDo8ExRVBIIdwIGrqpxHuKttA==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/core": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.30.1.tgz", + "integrity": "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/core/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", + "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-grpc": { + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-grpc/-/exporter-logs-otlp-grpc-0.55.0.tgz", + "integrity": "sha512-ykqawCL0ILJWyCJlxCPSAlqQXZ6x2bQsxAVUu8S3z22XNqY5SMx0rl2d93XnvnrOwtcfm+sM9ZhbGh/i5AZ9xw==", + "license": "Apache-2.0", + "dependencies": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "1.28.0", + "@opentelemetry/otlp-grpc-exporter-base": "0.55.0", + "@opentelemetry/otlp-transformer": "0.55.0", + "@opentelemetry/sdk-logs": "0.55.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-grpc/node_modules/@opentelemetry/api-logs": { + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.55.0.tgz", + "integrity": "sha512-3cpa+qI45VHYcA5c0bHM6VHo9gicv3p5mlLHNG3rLyjQU8b7e0st1rWtrUn3JbZ3DwwCfhKop4eQ9UuYlC6Pkg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-grpc/node_modules/@opentelemetry/core": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.28.0.tgz", + "integrity": "sha512-ZLwRMV+fNDpVmF2WYUdBHlq0eOWtEaUJSusrzjGnBt7iSRvfjFE3RXYUZJrqou/wIDWV0DwQ5KIfYe9WXg9Xqw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-grpc/node_modules/@opentelemetry/otlp-transformer": { + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.55.0.tgz", + "integrity": "sha512-kVqEfxtp6mSN2Dhpy0REo1ghP4PYhC1kMHQJ2qVlO99Pc+aigELjZDfg7/YKmL71gR6wVGIeJfiql/eXL7sQPA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.55.0", + "@opentelemetry/core": "1.28.0", + "@opentelemetry/resources": "1.28.0", + "@opentelemetry/sdk-logs": "0.55.0", + "@opentelemetry/sdk-metrics": "1.28.0", + "@opentelemetry/sdk-trace-base": "1.28.0", + "protobufjs": "^7.3.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-grpc/node_modules/@opentelemetry/resources": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.28.0.tgz", + "integrity": "sha512-cIyXSVJjGeTICENN40YSvLDAq4Y2502hGK3iN7tfdynQLKWb3XWZQEkPc+eSx47kiy11YeFAlYkEfXwR1w8kfw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.28.0", + "@opentelemetry/semantic-conventions": "1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-grpc/node_modules/@opentelemetry/sdk-logs": { + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.55.0.tgz", + "integrity": "sha512-TSx+Yg/d48uWW6HtjS1AD5x6WPfLhDWLl/WxC7I2fMevaiBuKCuraxTB8MDXieCNnBI24bw9ytyXrDCswFfWgA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.55.0", + "@opentelemetry/core": "1.28.0", + "@opentelemetry/resources": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.4.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-grpc/node_modules/@opentelemetry/sdk-metrics": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.28.0.tgz", + "integrity": "sha512-43tqMK/0BcKTyOvm15/WQ3HLr0Vu/ucAl/D84NO7iSlv6O4eOprxSHa3sUtmYkaZWHqdDJV0AHVz/R6u4JALVQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.28.0", + "@opentelemetry/resources": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-grpc/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.28.0.tgz", + "integrity": "sha512-ceUVWuCpIao7Y5xE02Xs3nQi0tOGmMea17ecBdwtCvdo9ekmO+ijc9RFDgfifMl7XCBf41zne/1POM3LqSTZDA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.28.0", + "@opentelemetry/resources": "1.28.0", + "@opentelemetry/semantic-conventions": "1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-grpc/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.27.0.tgz", + "integrity": "sha512-sAay1RrB+ONOem0OZanAR1ZI/k7yDpnOQSQmTMuGImUQb2y8EbSaCJ94FQluM74xoU03vlb2d2U90hZluL6nQg==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-http": { + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-http/-/exporter-logs-otlp-http-0.55.0.tgz", + "integrity": "sha512-fpFObWWq+DoLVrBU2dyMEaVkibByEkmKQZIUIjW/4j7lwIsTgW7aJCoD9RYFVB/tButcqov5Es2C0J2wTjM2tg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.55.0", + "@opentelemetry/core": "1.28.0", + "@opentelemetry/otlp-exporter-base": "0.55.0", + "@opentelemetry/otlp-transformer": "0.55.0", + "@opentelemetry/sdk-logs": "0.55.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-http/node_modules/@opentelemetry/api-logs": { + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.55.0.tgz", + "integrity": "sha512-3cpa+qI45VHYcA5c0bHM6VHo9gicv3p5mlLHNG3rLyjQU8b7e0st1rWtrUn3JbZ3DwwCfhKop4eQ9UuYlC6Pkg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-http/node_modules/@opentelemetry/core": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.28.0.tgz", + "integrity": "sha512-ZLwRMV+fNDpVmF2WYUdBHlq0eOWtEaUJSusrzjGnBt7iSRvfjFE3RXYUZJrqou/wIDWV0DwQ5KIfYe9WXg9Xqw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-http/node_modules/@opentelemetry/otlp-exporter-base": { + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.55.0.tgz", + "integrity": "sha512-iHQI0Zzq3h1T6xUJTVFwmFl5Dt5y1es+fl4kM+k5T/3YvmVyeYkSiF+wHCg6oKrlUAJfk+t55kaAu3sYmt7ZYA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.28.0", + "@opentelemetry/otlp-transformer": "0.55.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-http/node_modules/@opentelemetry/otlp-transformer": { + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.55.0.tgz", + "integrity": "sha512-kVqEfxtp6mSN2Dhpy0REo1ghP4PYhC1kMHQJ2qVlO99Pc+aigELjZDfg7/YKmL71gR6wVGIeJfiql/eXL7sQPA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.55.0", + "@opentelemetry/core": "1.28.0", + "@opentelemetry/resources": "1.28.0", + "@opentelemetry/sdk-logs": "0.55.0", + "@opentelemetry/sdk-metrics": "1.28.0", + "@opentelemetry/sdk-trace-base": "1.28.0", + "protobufjs": "^7.3.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-http/node_modules/@opentelemetry/resources": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.28.0.tgz", + "integrity": "sha512-cIyXSVJjGeTICENN40YSvLDAq4Y2502hGK3iN7tfdynQLKWb3XWZQEkPc+eSx47kiy11YeFAlYkEfXwR1w8kfw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.28.0", + "@opentelemetry/semantic-conventions": "1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-http/node_modules/@opentelemetry/sdk-logs": { + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.55.0.tgz", + "integrity": "sha512-TSx+Yg/d48uWW6HtjS1AD5x6WPfLhDWLl/WxC7I2fMevaiBuKCuraxTB8MDXieCNnBI24bw9ytyXrDCswFfWgA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.55.0", + "@opentelemetry/core": "1.28.0", + "@opentelemetry/resources": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.4.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-http/node_modules/@opentelemetry/sdk-metrics": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.28.0.tgz", + "integrity": "sha512-43tqMK/0BcKTyOvm15/WQ3HLr0Vu/ucAl/D84NO7iSlv6O4eOprxSHa3sUtmYkaZWHqdDJV0AHVz/R6u4JALVQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.28.0", + "@opentelemetry/resources": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-http/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.28.0.tgz", + "integrity": "sha512-ceUVWuCpIao7Y5xE02Xs3nQi0tOGmMea17ecBdwtCvdo9ekmO+ijc9RFDgfifMl7XCBf41zne/1POM3LqSTZDA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.28.0", + "@opentelemetry/resources": "1.28.0", + "@opentelemetry/semantic-conventions": "1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-http/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.27.0.tgz", + "integrity": "sha512-sAay1RrB+ONOem0OZanAR1ZI/k7yDpnOQSQmTMuGImUQb2y8EbSaCJ94FQluM74xoU03vlb2d2U90hZluL6nQg==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-proto": { + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-proto/-/exporter-logs-otlp-proto-0.55.0.tgz", + "integrity": "sha512-vjE+DxUr+cUpxikdKCPiLZM5Wx7g1bywjCG76TQocvsA7Tmbb9p0t1+8gPlu9AGH7VEzPwDxxpN4p1ajpOurzQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.55.0", + "@opentelemetry/core": "1.28.0", + "@opentelemetry/otlp-exporter-base": "0.55.0", + "@opentelemetry/otlp-transformer": "0.55.0", + "@opentelemetry/resources": "1.28.0", + "@opentelemetry/sdk-logs": "0.55.0", + "@opentelemetry/sdk-trace-base": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-proto/node_modules/@opentelemetry/api-logs": { + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.55.0.tgz", + "integrity": "sha512-3cpa+qI45VHYcA5c0bHM6VHo9gicv3p5mlLHNG3rLyjQU8b7e0st1rWtrUn3JbZ3DwwCfhKop4eQ9UuYlC6Pkg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-proto/node_modules/@opentelemetry/core": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.28.0.tgz", + "integrity": "sha512-ZLwRMV+fNDpVmF2WYUdBHlq0eOWtEaUJSusrzjGnBt7iSRvfjFE3RXYUZJrqou/wIDWV0DwQ5KIfYe9WXg9Xqw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-proto/node_modules/@opentelemetry/otlp-exporter-base": { + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.55.0.tgz", + "integrity": "sha512-iHQI0Zzq3h1T6xUJTVFwmFl5Dt5y1es+fl4kM+k5T/3YvmVyeYkSiF+wHCg6oKrlUAJfk+t55kaAu3sYmt7ZYA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.28.0", + "@opentelemetry/otlp-transformer": "0.55.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-proto/node_modules/@opentelemetry/otlp-transformer": { + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.55.0.tgz", + "integrity": "sha512-kVqEfxtp6mSN2Dhpy0REo1ghP4PYhC1kMHQJ2qVlO99Pc+aigELjZDfg7/YKmL71gR6wVGIeJfiql/eXL7sQPA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.55.0", + "@opentelemetry/core": "1.28.0", + "@opentelemetry/resources": "1.28.0", + "@opentelemetry/sdk-logs": "0.55.0", + "@opentelemetry/sdk-metrics": "1.28.0", + "@opentelemetry/sdk-trace-base": "1.28.0", + "protobufjs": "^7.3.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-proto/node_modules/@opentelemetry/resources": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.28.0.tgz", + "integrity": "sha512-cIyXSVJjGeTICENN40YSvLDAq4Y2502hGK3iN7tfdynQLKWb3XWZQEkPc+eSx47kiy11YeFAlYkEfXwR1w8kfw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.28.0", + "@opentelemetry/semantic-conventions": "1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-proto/node_modules/@opentelemetry/sdk-logs": { + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.55.0.tgz", + "integrity": "sha512-TSx+Yg/d48uWW6HtjS1AD5x6WPfLhDWLl/WxC7I2fMevaiBuKCuraxTB8MDXieCNnBI24bw9ytyXrDCswFfWgA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.55.0", + "@opentelemetry/core": "1.28.0", + "@opentelemetry/resources": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.4.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-proto/node_modules/@opentelemetry/sdk-metrics": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.28.0.tgz", + "integrity": "sha512-43tqMK/0BcKTyOvm15/WQ3HLr0Vu/ucAl/D84NO7iSlv6O4eOprxSHa3sUtmYkaZWHqdDJV0AHVz/R6u4JALVQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.28.0", + "@opentelemetry/resources": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-proto/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.28.0.tgz", + "integrity": "sha512-ceUVWuCpIao7Y5xE02Xs3nQi0tOGmMea17ecBdwtCvdo9ekmO+ijc9RFDgfifMl7XCBf41zne/1POM3LqSTZDA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.28.0", + "@opentelemetry/resources": "1.28.0", + "@opentelemetry/semantic-conventions": "1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-proto/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.27.0.tgz", + "integrity": "sha512-sAay1RrB+ONOem0OZanAR1ZI/k7yDpnOQSQmTMuGImUQb2y8EbSaCJ94FQluM74xoU03vlb2d2U90hZluL6nQg==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-http": { + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-http/-/exporter-metrics-otlp-http-0.55.0.tgz", + "integrity": "sha512-3MqDNZzgXmLaiVo9gs9kCw/zPEaZYKIT0+jeMWscWHL/jrA9BNArTOYWUHEPabAQmWQ2BbvgNC7yzlqjoynQwA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.28.0", + "@opentelemetry/otlp-exporter-base": "0.55.0", + "@opentelemetry/otlp-transformer": "0.55.0", + "@opentelemetry/resources": "1.28.0", + "@opentelemetry/sdk-metrics": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-http/node_modules/@opentelemetry/api-logs": { + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.55.0.tgz", + "integrity": "sha512-3cpa+qI45VHYcA5c0bHM6VHo9gicv3p5mlLHNG3rLyjQU8b7e0st1rWtrUn3JbZ3DwwCfhKop4eQ9UuYlC6Pkg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-http/node_modules/@opentelemetry/core": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.28.0.tgz", + "integrity": "sha512-ZLwRMV+fNDpVmF2WYUdBHlq0eOWtEaUJSusrzjGnBt7iSRvfjFE3RXYUZJrqou/wIDWV0DwQ5KIfYe9WXg9Xqw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-http/node_modules/@opentelemetry/otlp-exporter-base": { + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.55.0.tgz", + "integrity": "sha512-iHQI0Zzq3h1T6xUJTVFwmFl5Dt5y1es+fl4kM+k5T/3YvmVyeYkSiF+wHCg6oKrlUAJfk+t55kaAu3sYmt7ZYA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.28.0", + "@opentelemetry/otlp-transformer": "0.55.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-http/node_modules/@opentelemetry/otlp-transformer": { + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.55.0.tgz", + "integrity": "sha512-kVqEfxtp6mSN2Dhpy0REo1ghP4PYhC1kMHQJ2qVlO99Pc+aigELjZDfg7/YKmL71gR6wVGIeJfiql/eXL7sQPA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.55.0", + "@opentelemetry/core": "1.28.0", + "@opentelemetry/resources": "1.28.0", + "@opentelemetry/sdk-logs": "0.55.0", + "@opentelemetry/sdk-metrics": "1.28.0", + "@opentelemetry/sdk-trace-base": "1.28.0", + "protobufjs": "^7.3.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-http/node_modules/@opentelemetry/resources": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.28.0.tgz", + "integrity": "sha512-cIyXSVJjGeTICENN40YSvLDAq4Y2502hGK3iN7tfdynQLKWb3XWZQEkPc+eSx47kiy11YeFAlYkEfXwR1w8kfw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.28.0", + "@opentelemetry/semantic-conventions": "1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-http/node_modules/@opentelemetry/sdk-logs": { + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.55.0.tgz", + "integrity": "sha512-TSx+Yg/d48uWW6HtjS1AD5x6WPfLhDWLl/WxC7I2fMevaiBuKCuraxTB8MDXieCNnBI24bw9ytyXrDCswFfWgA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.55.0", + "@opentelemetry/core": "1.28.0", + "@opentelemetry/resources": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.4.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-http/node_modules/@opentelemetry/sdk-metrics": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.28.0.tgz", + "integrity": "sha512-43tqMK/0BcKTyOvm15/WQ3HLr0Vu/ucAl/D84NO7iSlv6O4eOprxSHa3sUtmYkaZWHqdDJV0AHVz/R6u4JALVQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.28.0", + "@opentelemetry/resources": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-http/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.28.0.tgz", + "integrity": "sha512-ceUVWuCpIao7Y5xE02Xs3nQi0tOGmMea17ecBdwtCvdo9ekmO+ijc9RFDgfifMl7XCBf41zne/1POM3LqSTZDA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.28.0", + "@opentelemetry/resources": "1.28.0", + "@opentelemetry/semantic-conventions": "1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-http/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.27.0.tgz", + "integrity": "sha512-sAay1RrB+ONOem0OZanAR1ZI/k7yDpnOQSQmTMuGImUQb2y8EbSaCJ94FQluM74xoU03vlb2d2U90hZluL6nQg==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc": { + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-grpc/-/exporter-trace-otlp-grpc-0.55.0.tgz", + "integrity": "sha512-ohIkCLn2Wc3vhhFuf1bH8kOXHMEdcWiD847x7f3Qfygc+CGiatGLzQYscTcEYsWGMV22gVwB/kVcNcx5a3o8gA==", + "license": "Apache-2.0", + "dependencies": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "1.28.0", + "@opentelemetry/otlp-grpc-exporter-base": "0.55.0", + "@opentelemetry/otlp-transformer": "0.55.0", + "@opentelemetry/resources": "1.28.0", + "@opentelemetry/sdk-trace-base": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/api-logs": { + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.55.0.tgz", + "integrity": "sha512-3cpa+qI45VHYcA5c0bHM6VHo9gicv3p5mlLHNG3rLyjQU8b7e0st1rWtrUn3JbZ3DwwCfhKop4eQ9UuYlC6Pkg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/core": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.28.0.tgz", + "integrity": "sha512-ZLwRMV+fNDpVmF2WYUdBHlq0eOWtEaUJSusrzjGnBt7iSRvfjFE3RXYUZJrqou/wIDWV0DwQ5KIfYe9WXg9Xqw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/otlp-transformer": { + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.55.0.tgz", + "integrity": "sha512-kVqEfxtp6mSN2Dhpy0REo1ghP4PYhC1kMHQJ2qVlO99Pc+aigELjZDfg7/YKmL71gR6wVGIeJfiql/eXL7sQPA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.55.0", + "@opentelemetry/core": "1.28.0", + "@opentelemetry/resources": "1.28.0", + "@opentelemetry/sdk-logs": "0.55.0", + "@opentelemetry/sdk-metrics": "1.28.0", + "@opentelemetry/sdk-trace-base": "1.28.0", + "protobufjs": "^7.3.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/resources": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.28.0.tgz", + "integrity": "sha512-cIyXSVJjGeTICENN40YSvLDAq4Y2502hGK3iN7tfdynQLKWb3XWZQEkPc+eSx47kiy11YeFAlYkEfXwR1w8kfw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.28.0", + "@opentelemetry/semantic-conventions": "1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/sdk-logs": { + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.55.0.tgz", + "integrity": "sha512-TSx+Yg/d48uWW6HtjS1AD5x6WPfLhDWLl/WxC7I2fMevaiBuKCuraxTB8MDXieCNnBI24bw9ytyXrDCswFfWgA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.55.0", + "@opentelemetry/core": "1.28.0", + "@opentelemetry/resources": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.4.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/sdk-metrics": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.28.0.tgz", + "integrity": "sha512-43tqMK/0BcKTyOvm15/WQ3HLr0Vu/ucAl/D84NO7iSlv6O4eOprxSHa3sUtmYkaZWHqdDJV0AHVz/R6u4JALVQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.28.0", + "@opentelemetry/resources": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.28.0.tgz", + "integrity": "sha512-ceUVWuCpIao7Y5xE02Xs3nQi0tOGmMea17ecBdwtCvdo9ekmO+ijc9RFDgfifMl7XCBf41zne/1POM3LqSTZDA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.28.0", + "@opentelemetry/resources": "1.28.0", + "@opentelemetry/semantic-conventions": "1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.27.0.tgz", + "integrity": "sha512-sAay1RrB+ONOem0OZanAR1ZI/k7yDpnOQSQmTMuGImUQb2y8EbSaCJ94FQluM74xoU03vlb2d2U90hZluL6nQg==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http": { + "version": "0.57.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.57.1.tgz", + "integrity": "sha512-43dLEjlf6JGxpVt9RaRlJAvjHG1wGsbAuNd67RIDy/95zfKk2aNovtiGUgFdS/kcvgvS90upIUbgn0xUd9JjMg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/otlp-exporter-base": "0.57.1", + "@opentelemetry/otlp-transformer": "0.57.1", + "@opentelemetry/resources": "1.30.1", + "@opentelemetry/sdk-trace-base": "1.30.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto": { + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-proto/-/exporter-trace-otlp-proto-0.55.0.tgz", + "integrity": "sha512-qxiJFP+bBZW3+goHCGkE1ZdW9gJU0fR7eQ6OP+Rz5oGtEBbq4nkGodhb7C9FJlEFlE2siPtCxoeupV0gtYynag==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.28.0", + "@opentelemetry/otlp-exporter-base": "0.55.0", + "@opentelemetry/otlp-transformer": "0.55.0", + "@opentelemetry/resources": "1.28.0", + "@opentelemetry/sdk-trace-base": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/api-logs": { + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.55.0.tgz", + "integrity": "sha512-3cpa+qI45VHYcA5c0bHM6VHo9gicv3p5mlLHNG3rLyjQU8b7e0st1rWtrUn3JbZ3DwwCfhKop4eQ9UuYlC6Pkg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/core": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.28.0.tgz", + "integrity": "sha512-ZLwRMV+fNDpVmF2WYUdBHlq0eOWtEaUJSusrzjGnBt7iSRvfjFE3RXYUZJrqou/wIDWV0DwQ5KIfYe9WXg9Xqw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/otlp-exporter-base": { + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.55.0.tgz", + "integrity": "sha512-iHQI0Zzq3h1T6xUJTVFwmFl5Dt5y1es+fl4kM+k5T/3YvmVyeYkSiF+wHCg6oKrlUAJfk+t55kaAu3sYmt7ZYA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.28.0", + "@opentelemetry/otlp-transformer": "0.55.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/otlp-transformer": { + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.55.0.tgz", + "integrity": "sha512-kVqEfxtp6mSN2Dhpy0REo1ghP4PYhC1kMHQJ2qVlO99Pc+aigELjZDfg7/YKmL71gR6wVGIeJfiql/eXL7sQPA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.55.0", + "@opentelemetry/core": "1.28.0", + "@opentelemetry/resources": "1.28.0", + "@opentelemetry/sdk-logs": "0.55.0", + "@opentelemetry/sdk-metrics": "1.28.0", + "@opentelemetry/sdk-trace-base": "1.28.0", + "protobufjs": "^7.3.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/resources": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.28.0.tgz", + "integrity": "sha512-cIyXSVJjGeTICENN40YSvLDAq4Y2502hGK3iN7tfdynQLKWb3XWZQEkPc+eSx47kiy11YeFAlYkEfXwR1w8kfw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.28.0", + "@opentelemetry/semantic-conventions": "1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/sdk-logs": { + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.55.0.tgz", + "integrity": "sha512-TSx+Yg/d48uWW6HtjS1AD5x6WPfLhDWLl/WxC7I2fMevaiBuKCuraxTB8MDXieCNnBI24bw9ytyXrDCswFfWgA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.55.0", + "@opentelemetry/core": "1.28.0", + "@opentelemetry/resources": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.4.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/sdk-metrics": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.28.0.tgz", + "integrity": "sha512-43tqMK/0BcKTyOvm15/WQ3HLr0Vu/ucAl/D84NO7iSlv6O4eOprxSHa3sUtmYkaZWHqdDJV0AHVz/R6u4JALVQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.28.0", + "@opentelemetry/resources": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.28.0.tgz", + "integrity": "sha512-ceUVWuCpIao7Y5xE02Xs3nQi0tOGmMea17ecBdwtCvdo9ekmO+ijc9RFDgfifMl7XCBf41zne/1POM3LqSTZDA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.28.0", + "@opentelemetry/resources": "1.28.0", + "@opentelemetry/semantic-conventions": "1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.27.0.tgz", + "integrity": "sha512-sAay1RrB+ONOem0OZanAR1ZI/k7yDpnOQSQmTMuGImUQb2y8EbSaCJ94FQluM74xoU03vlb2d2U90hZluL6nQg==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/exporter-zipkin": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-zipkin/-/exporter-zipkin-1.30.1.tgz", + "integrity": "sha512-6S2QIMJahIquvFaaxmcwpvQQRD/YFaMTNoIxrfPIPOeITN+a8lfEcPDxNxn8JDAaxkg+4EnXhz8upVDYenoQjA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/resources": "1.30.1", + "@opentelemetry/sdk-trace-base": "1.30.1", + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", + "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/id-generator-aws-xray": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/id-generator-aws-xray/-/id-generator-aws-xray-1.2.2.tgz", + "integrity": "sha512-IKTqub5m/yI9+Cn4LJS+GbQrG7Ode9OWAmfJENmCMFTR5J2qGM7VOFaNpQFMICr7Wo8SUtgoJNbTxaQZ9AdMMA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^1.0.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/instrumentation": { + "version": "0.57.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.57.1.tgz", + "integrity": "sha512-SgHEKXoVxOjc20ZYusPG3Fh+RLIZTSa4x8QtD3NfgAUDyqdFFS9W1F2ZVbZkqDCdyMcQG02Ok4duUGLHJXHgbA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.57.1", + "@types/shimmer": "^1.2.0", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-amqplib": { + "version": "0.46.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-amqplib/-/instrumentation-amqplib-0.46.0.tgz", + "integrity": "sha512-04VHHV1KIN/c1wLWwzmLI02d/welgscBJ4BuDqrHaxd+ZIdlVXK9UYQsYf3JwSeF52z/4YoSzr8bfdVBSWoMAg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.57.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-aws-lambda": { + "version": "0.47.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-aws-lambda/-/instrumentation-aws-lambda-0.47.0.tgz", + "integrity": "sha512-0BidKDPziHWGl5mnpLuh7ob1X3KpR0UN3QcJkcxIsOMylBbMMp9EoB55dHsTMoNO7bx2uyeY0iirEuTchjF1gQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.54.0", + "@opentelemetry/semantic-conventions": "^1.27.0", + "@types/aws-lambda": "8.10.143" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-aws-lambda/node_modules/@opentelemetry/api-logs": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.54.2.tgz", + "integrity": "sha512-4MTVwwmLgUh5QrJnZpYo6YRO5IBLAggf2h8gWDblwRagDStY13aEvt7gGk3jewrMaPlHiF83fENhIx0HO97/cQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation-aws-lambda/node_modules/@opentelemetry/instrumentation": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.54.2.tgz", + "integrity": "sha512-go6zpOVoZVztT9r1aPd79Fr3OWiD4N24bCPJsIKkBses8oyFo12F/Ew3UBTdIu6hsW4HC4MVEJygG6TEyJI/lg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.54.2", + "@types/shimmer": "^1.2.0", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-aws-sdk": { + "version": "0.46.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-aws-sdk/-/instrumentation-aws-sdk-0.46.0.tgz", + "integrity": "sha512-EyxGQVYhgY8OI4/CKzqamUswiEVlua6DJcsmkeNSykZrDGs78jPfssbqoMQGetywHWPZBRVJN4Ba/7aB5iLHBA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.54.0", + "@opentelemetry/propagation-utils": "^0.30.12", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-aws-sdk/node_modules/@opentelemetry/api-logs": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.54.2.tgz", + "integrity": "sha512-4MTVwwmLgUh5QrJnZpYo6YRO5IBLAggf2h8gWDblwRagDStY13aEvt7gGk3jewrMaPlHiF83fENhIx0HO97/cQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation-aws-sdk/node_modules/@opentelemetry/instrumentation": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.54.2.tgz", + "integrity": "sha512-go6zpOVoZVztT9r1aPd79Fr3OWiD4N24bCPJsIKkBses8oyFo12F/Ew3UBTdIu6hsW4HC4MVEJygG6TEyJI/lg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.54.2", + "@types/shimmer": "^1.2.0", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-bunyan": { + "version": "0.42.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-bunyan/-/instrumentation-bunyan-0.42.0.tgz", + "integrity": "sha512-GBh6ybwKmFZjc86SyHVx72jHg+4pFPaXT3IZgJ4QtnMsMf0/q5m2aHAjid+yakmEkApsnRWX8pJ8nkl1e+6mag==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "^0.54.0", + "@opentelemetry/instrumentation": "^0.54.0", + "@types/bunyan": "1.8.9" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-bunyan/node_modules/@opentelemetry/api-logs": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.54.2.tgz", + "integrity": "sha512-4MTVwwmLgUh5QrJnZpYo6YRO5IBLAggf2h8gWDblwRagDStY13aEvt7gGk3jewrMaPlHiF83fENhIx0HO97/cQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation-bunyan/node_modules/@opentelemetry/instrumentation": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.54.2.tgz", + "integrity": "sha512-go6zpOVoZVztT9r1aPd79Fr3OWiD4N24bCPJsIKkBses8oyFo12F/Ew3UBTdIu6hsW4HC4MVEJygG6TEyJI/lg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.54.2", + "@types/shimmer": "^1.2.0", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-cassandra-driver": { + "version": "0.42.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-cassandra-driver/-/instrumentation-cassandra-driver-0.42.0.tgz", + "integrity": "sha512-35I9Gw4BeSs9NPe7fugu9e/mWKaapc/N1wounHnGt259/Q3ISGMOQRrOwIBw+x/XJygJvn4Ss1c+r5h89TsVAw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.54.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-cassandra-driver/node_modules/@opentelemetry/api-logs": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.54.2.tgz", + "integrity": "sha512-4MTVwwmLgUh5QrJnZpYo6YRO5IBLAggf2h8gWDblwRagDStY13aEvt7gGk3jewrMaPlHiF83fENhIx0HO97/cQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation-cassandra-driver/node_modules/@opentelemetry/instrumentation": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.54.2.tgz", + "integrity": "sha512-go6zpOVoZVztT9r1aPd79Fr3OWiD4N24bCPJsIKkBses8oyFo12F/Ew3UBTdIu6hsW4HC4MVEJygG6TEyJI/lg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.54.2", + "@types/shimmer": "^1.2.0", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-connect": { + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-connect/-/instrumentation-connect-0.40.0.tgz", + "integrity": "sha512-3aR/3YBQ160siitwwRLjwqrv2KBT16897+bo6yz8wIfel6nWOxTZBJudcbsK3p42pTC7qrbotJ9t/1wRLpv79Q==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.54.0", + "@opentelemetry/semantic-conventions": "^1.27.0", + "@types/connect": "3.4.36" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-connect/node_modules/@opentelemetry/api-logs": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.54.2.tgz", + "integrity": "sha512-4MTVwwmLgUh5QrJnZpYo6YRO5IBLAggf2h8gWDblwRagDStY13aEvt7gGk3jewrMaPlHiF83fENhIx0HO97/cQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation-connect/node_modules/@opentelemetry/instrumentation": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.54.2.tgz", + "integrity": "sha512-go6zpOVoZVztT9r1aPd79Fr3OWiD4N24bCPJsIKkBses8oyFo12F/Ew3UBTdIu6hsW4HC4MVEJygG6TEyJI/lg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.54.2", + "@types/shimmer": "^1.2.0", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-cucumber": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-cucumber/-/instrumentation-cucumber-0.10.0.tgz", + "integrity": "sha512-5sT6Ap3W7StEL0Oax/vd1YTEcTPTefx+9myzkKrr72hxzFzSooGRCxlU3sfPwZqWptUV7+QWTMd7SqGEEPnE/w==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.54.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/instrumentation-cucumber/node_modules/@opentelemetry/api-logs": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.54.2.tgz", + "integrity": "sha512-4MTVwwmLgUh5QrJnZpYo6YRO5IBLAggf2h8gWDblwRagDStY13aEvt7gGk3jewrMaPlHiF83fENhIx0HO97/cQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation-cucumber/node_modules/@opentelemetry/instrumentation": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.54.2.tgz", + "integrity": "sha512-go6zpOVoZVztT9r1aPd79Fr3OWiD4N24bCPJsIKkBses8oyFo12F/Ew3UBTdIu6hsW4HC4MVEJygG6TEyJI/lg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.54.2", + "@types/shimmer": "^1.2.0", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-dataloader": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-dataloader/-/instrumentation-dataloader-0.13.0.tgz", + "integrity": "sha512-wbU3WdgUAXljEIY2nfpkqID/VH70ThnES8mZZHKCZlV/Pl5T4+qmrVdT7U9/WUzz8flwsXfER6T6jl48Wbl+LQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.54.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-dataloader/node_modules/@opentelemetry/api-logs": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.54.2.tgz", + "integrity": "sha512-4MTVwwmLgUh5QrJnZpYo6YRO5IBLAggf2h8gWDblwRagDStY13aEvt7gGk3jewrMaPlHiF83fENhIx0HO97/cQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation-dataloader/node_modules/@opentelemetry/instrumentation": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.54.2.tgz", + "integrity": "sha512-go6zpOVoZVztT9r1aPd79Fr3OWiD4N24bCPJsIKkBses8oyFo12F/Ew3UBTdIu6hsW4HC4MVEJygG6TEyJI/lg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.54.2", + "@types/shimmer": "^1.2.0", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-dns": { + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-dns/-/instrumentation-dns-0.40.0.tgz", + "integrity": "sha512-tLNR8XLPiYRKKk3/UqifXnPP2TVt1RcwvHU0R1ETL1xkZ1ZHMTmSC4x6TignnHOFtRixtJ05EgMGejnffaBXkQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.54.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-dns/node_modules/@opentelemetry/api-logs": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.54.2.tgz", + "integrity": "sha512-4MTVwwmLgUh5QrJnZpYo6YRO5IBLAggf2h8gWDblwRagDStY13aEvt7gGk3jewrMaPlHiF83fENhIx0HO97/cQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation-dns/node_modules/@opentelemetry/instrumentation": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.54.2.tgz", + "integrity": "sha512-go6zpOVoZVztT9r1aPd79Fr3OWiD4N24bCPJsIKkBses8oyFo12F/Ew3UBTdIu6hsW4HC4MVEJygG6TEyJI/lg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.54.2", + "@types/shimmer": "^1.2.0", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-express": { + "version": "0.44.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-express/-/instrumentation-express-0.44.0.tgz", + "integrity": "sha512-GWgibp6Q0wxyFaaU8ERIgMMYgzcHmGrw3ILUtGchLtLncHNOKk0SNoWGqiylXWWT4HTn5XdV8MGawUgpZh80cA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.54.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-express/node_modules/@opentelemetry/api-logs": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.54.2.tgz", + "integrity": "sha512-4MTVwwmLgUh5QrJnZpYo6YRO5IBLAggf2h8gWDblwRagDStY13aEvt7gGk3jewrMaPlHiF83fENhIx0HO97/cQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation-express/node_modules/@opentelemetry/instrumentation": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.54.2.tgz", + "integrity": "sha512-go6zpOVoZVztT9r1aPd79Fr3OWiD4N24bCPJsIKkBses8oyFo12F/Ew3UBTdIu6hsW4HC4MVEJygG6TEyJI/lg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.54.2", + "@types/shimmer": "^1.2.0", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-fastify": { + "version": "0.41.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-fastify/-/instrumentation-fastify-0.41.0.tgz", + "integrity": "sha512-pNRjFvf0mvqfJueaeL/qEkuGJwgtE5pgjIHGYwjc2rMViNCrtY9/Sf+Nu8ww6dDd/Oyk2fwZZP7i0XZfCnETrA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.54.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-fastify/node_modules/@opentelemetry/api-logs": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.54.2.tgz", + "integrity": "sha512-4MTVwwmLgUh5QrJnZpYo6YRO5IBLAggf2h8gWDblwRagDStY13aEvt7gGk3jewrMaPlHiF83fENhIx0HO97/cQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation-fastify/node_modules/@opentelemetry/instrumentation": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.54.2.tgz", + "integrity": "sha512-go6zpOVoZVztT9r1aPd79Fr3OWiD4N24bCPJsIKkBses8oyFo12F/Ew3UBTdIu6hsW4HC4MVEJygG6TEyJI/lg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.54.2", + "@types/shimmer": "^1.2.0", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-fs": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-fs/-/instrumentation-fs-0.16.0.tgz", + "integrity": "sha512-hMDRUxV38ln1R3lNz6osj3YjlO32ykbHqVrzG7gEhGXFQfu7LJUx8t9tEwE4r2h3CD4D0Rw4YGDU4yF4mP3ilg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.54.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-fs/node_modules/@opentelemetry/api-logs": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.54.2.tgz", + "integrity": "sha512-4MTVwwmLgUh5QrJnZpYo6YRO5IBLAggf2h8gWDblwRagDStY13aEvt7gGk3jewrMaPlHiF83fENhIx0HO97/cQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation-fs/node_modules/@opentelemetry/instrumentation": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.54.2.tgz", + "integrity": "sha512-go6zpOVoZVztT9r1aPd79Fr3OWiD4N24bCPJsIKkBses8oyFo12F/Ew3UBTdIu6hsW4HC4MVEJygG6TEyJI/lg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.54.2", + "@types/shimmer": "^1.2.0", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-generic-pool": { + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-generic-pool/-/instrumentation-generic-pool-0.40.0.tgz", + "integrity": "sha512-k+/JlNDHN3bPi/Cir+Ew6tKHFVCa1ZFeQyGUw5HQkRX/twCRaN3kJFXJW+rDAN90XwK3RtC9AWwBihDGh/oSlQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.54.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-generic-pool/node_modules/@opentelemetry/api-logs": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.54.2.tgz", + "integrity": "sha512-4MTVwwmLgUh5QrJnZpYo6YRO5IBLAggf2h8gWDblwRagDStY13aEvt7gGk3jewrMaPlHiF83fENhIx0HO97/cQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation-generic-pool/node_modules/@opentelemetry/instrumentation": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.54.2.tgz", + "integrity": "sha512-go6zpOVoZVztT9r1aPd79Fr3OWiD4N24bCPJsIKkBses8oyFo12F/Ew3UBTdIu6hsW4HC4MVEJygG6TEyJI/lg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.54.2", + "@types/shimmer": "^1.2.0", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-graphql": { + "version": "0.44.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-graphql/-/instrumentation-graphql-0.44.0.tgz", + "integrity": "sha512-FYXTe3Bv96aNpYktqm86BFUTpjglKD0kWI5T5bxYkLUPEPvFn38vWGMJTGrDMVou/i55E4jlWvcm6hFIqLsMbg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.54.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-graphql/node_modules/@opentelemetry/api-logs": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.54.2.tgz", + "integrity": "sha512-4MTVwwmLgUh5QrJnZpYo6YRO5IBLAggf2h8gWDblwRagDStY13aEvt7gGk3jewrMaPlHiF83fENhIx0HO97/cQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation-graphql/node_modules/@opentelemetry/instrumentation": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.54.2.tgz", + "integrity": "sha512-go6zpOVoZVztT9r1aPd79Fr3OWiD4N24bCPJsIKkBses8oyFo12F/Ew3UBTdIu6hsW4HC4MVEJygG6TEyJI/lg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.54.2", + "@types/shimmer": "^1.2.0", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-grpc": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-grpc/-/instrumentation-grpc-0.54.2.tgz", + "integrity": "sha512-KhSzerCaaqVH2zfDro7nTunWUZXt1pQISQpE83LuQTOKGk7mN3G60T1wliQ3Qdg0X3UUuhCXEC7u6IAVfDxkUQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "0.54.2", + "@opentelemetry/semantic-conventions": "1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-grpc/node_modules/@opentelemetry/api-logs": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.54.2.tgz", + "integrity": "sha512-4MTVwwmLgUh5QrJnZpYo6YRO5IBLAggf2h8gWDblwRagDStY13aEvt7gGk3jewrMaPlHiF83fENhIx0HO97/cQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation-grpc/node_modules/@opentelemetry/instrumentation": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.54.2.tgz", + "integrity": "sha512-go6zpOVoZVztT9r1aPd79Fr3OWiD4N24bCPJsIKkBses8oyFo12F/Ew3UBTdIu6hsW4HC4MVEJygG6TEyJI/lg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.54.2", + "@types/shimmer": "^1.2.0", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-grpc/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.27.0.tgz", + "integrity": "sha512-sAay1RrB+ONOem0OZanAR1ZI/k7yDpnOQSQmTMuGImUQb2y8EbSaCJ94FQluM74xoU03vlb2d2U90hZluL6nQg==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation-hapi": { + "version": "0.42.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-hapi/-/instrumentation-hapi-0.42.0.tgz", + "integrity": "sha512-TQC0BtIWLHrp6nKsYdZ5t5B7aiZ16BwbRqZtYYQxeJVsq/HQTANWpknjtA7KMxv5tAUMCrU/eDo8F3qioUOSZg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.54.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-hapi/node_modules/@opentelemetry/api-logs": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.54.2.tgz", + "integrity": "sha512-4MTVwwmLgUh5QrJnZpYo6YRO5IBLAggf2h8gWDblwRagDStY13aEvt7gGk3jewrMaPlHiF83fENhIx0HO97/cQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation-hapi/node_modules/@opentelemetry/instrumentation": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.54.2.tgz", + "integrity": "sha512-go6zpOVoZVztT9r1aPd79Fr3OWiD4N24bCPJsIKkBses8oyFo12F/Ew3UBTdIu6hsW4HC4MVEJygG6TEyJI/lg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.54.2", + "@types/shimmer": "^1.2.0", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-http": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-http/-/instrumentation-http-0.54.2.tgz", + "integrity": "sha512-mABjJ34UcU32pg8g18L9xBh0U3JON/2F6/57BYYy8AZJp2a71lZjcKr0T00pICoic50TW5HvcTrmyfMil+AiXQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.27.0", + "@opentelemetry/instrumentation": "0.54.2", + "@opentelemetry/semantic-conventions": "1.27.0", + "forwarded-parse": "2.1.2", + "semver": "^7.5.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-http/node_modules/@opentelemetry/api-logs": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.54.2.tgz", + "integrity": "sha512-4MTVwwmLgUh5QrJnZpYo6YRO5IBLAggf2h8gWDblwRagDStY13aEvt7gGk3jewrMaPlHiF83fENhIx0HO97/cQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation-http/node_modules/@opentelemetry/core": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.27.0.tgz", + "integrity": "sha512-yQPKnK5e+76XuiqUH/gKyS8wv/7qITd5ln56QkBTf3uggr0VkXOXfcaAuG330UfdYu83wsyoBwqwxigpIG+Jkg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/instrumentation-http/node_modules/@opentelemetry/instrumentation": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.54.2.tgz", + "integrity": "sha512-go6zpOVoZVztT9r1aPd79Fr3OWiD4N24bCPJsIKkBses8oyFo12F/Ew3UBTdIu6hsW4HC4MVEJygG6TEyJI/lg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.54.2", + "@types/shimmer": "^1.2.0", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-http/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.27.0.tgz", + "integrity": "sha512-sAay1RrB+ONOem0OZanAR1ZI/k7yDpnOQSQmTMuGImUQb2y8EbSaCJ94FQluM74xoU03vlb2d2U90hZluL6nQg==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation-ioredis": { + "version": "0.44.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-ioredis/-/instrumentation-ioredis-0.44.0.tgz", + "integrity": "sha512-312pE2xc0ihX9haTf9WC4OF9in5EfVO1y5I8Ef9aMQKJNhuSe3IgzQAqGoLfaYajC+ig0IZ9SQKU8mRbFwHU+A==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.54.0", + "@opentelemetry/redis-common": "^0.36.2", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-ioredis/node_modules/@opentelemetry/api-logs": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.54.2.tgz", + "integrity": "sha512-4MTVwwmLgUh5QrJnZpYo6YRO5IBLAggf2h8gWDblwRagDStY13aEvt7gGk3jewrMaPlHiF83fENhIx0HO97/cQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation-ioredis/node_modules/@opentelemetry/instrumentation": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.54.2.tgz", + "integrity": "sha512-go6zpOVoZVztT9r1aPd79Fr3OWiD4N24bCPJsIKkBses8oyFo12F/Ew3UBTdIu6hsW4HC4MVEJygG6TEyJI/lg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.54.2", + "@types/shimmer": "^1.2.0", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-kafkajs": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-kafkajs/-/instrumentation-kafkajs-0.4.0.tgz", + "integrity": "sha512-I9VwDG314g7SDL4t8kD/7+1ytaDBRbZQjhVaQaVIDR8K+mlsoBhLsWH79yHxhHQKvwCSZwqXF+TiTOhoQVUt7A==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.54.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-kafkajs/node_modules/@opentelemetry/api-logs": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.54.2.tgz", + "integrity": "sha512-4MTVwwmLgUh5QrJnZpYo6YRO5IBLAggf2h8gWDblwRagDStY13aEvt7gGk3jewrMaPlHiF83fENhIx0HO97/cQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation-kafkajs/node_modules/@opentelemetry/instrumentation": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.54.2.tgz", + "integrity": "sha512-go6zpOVoZVztT9r1aPd79Fr3OWiD4N24bCPJsIKkBses8oyFo12F/Ew3UBTdIu6hsW4HC4MVEJygG6TEyJI/lg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.54.2", + "@types/shimmer": "^1.2.0", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-knex": { + "version": "0.41.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-knex/-/instrumentation-knex-0.41.0.tgz", + "integrity": "sha512-OhI1SlLv5qnsnm2dOVrian/x3431P75GngSpnR7c4fcVFv7prXGYu29Z6ILRWJf/NJt6fkbySmwdfUUnFnHCTg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.54.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-knex/node_modules/@opentelemetry/api-logs": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.54.2.tgz", + "integrity": "sha512-4MTVwwmLgUh5QrJnZpYo6YRO5IBLAggf2h8gWDblwRagDStY13aEvt7gGk3jewrMaPlHiF83fENhIx0HO97/cQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation-knex/node_modules/@opentelemetry/instrumentation": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.54.2.tgz", + "integrity": "sha512-go6zpOVoZVztT9r1aPd79Fr3OWiD4N24bCPJsIKkBses8oyFo12F/Ew3UBTdIu6hsW4HC4MVEJygG6TEyJI/lg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.54.2", + "@types/shimmer": "^1.2.0", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-koa": { + "version": "0.44.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-koa/-/instrumentation-koa-0.44.0.tgz", + "integrity": "sha512-ryPqGIQ4hpMGd85bAGjRMDAy/ic+Qdh1GtFGJo9KaXdzbcvZoF1ZgXVsKTYDxbD1n5C0BoQy6rcWg8Lu68iCJA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.54.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-koa/node_modules/@opentelemetry/api-logs": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.54.2.tgz", + "integrity": "sha512-4MTVwwmLgUh5QrJnZpYo6YRO5IBLAggf2h8gWDblwRagDStY13aEvt7gGk3jewrMaPlHiF83fENhIx0HO97/cQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation-koa/node_modules/@opentelemetry/instrumentation": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.54.2.tgz", + "integrity": "sha512-go6zpOVoZVztT9r1aPd79Fr3OWiD4N24bCPJsIKkBses8oyFo12F/Ew3UBTdIu6hsW4HC4MVEJygG6TEyJI/lg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.54.2", + "@types/shimmer": "^1.2.0", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-lru-memoizer": { + "version": "0.41.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-lru-memoizer/-/instrumentation-lru-memoizer-0.41.0.tgz", + "integrity": "sha512-6OePkk4RYCPVsnS0TroEK6UZzxxxjVWaE6EPdOn2qxGHMtm+Qb80tiBQ6BbmC+f7bjc27O85JY8gxeTybhHZXw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.54.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-lru-memoizer/node_modules/@opentelemetry/api-logs": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.54.2.tgz", + "integrity": "sha512-4MTVwwmLgUh5QrJnZpYo6YRO5IBLAggf2h8gWDblwRagDStY13aEvt7gGk3jewrMaPlHiF83fENhIx0HO97/cQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation-lru-memoizer/node_modules/@opentelemetry/instrumentation": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.54.2.tgz", + "integrity": "sha512-go6zpOVoZVztT9r1aPd79Fr3OWiD4N24bCPJsIKkBses8oyFo12F/Ew3UBTdIu6hsW4HC4MVEJygG6TEyJI/lg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.54.2", + "@types/shimmer": "^1.2.0", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-memcached": { + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-memcached/-/instrumentation-memcached-0.40.0.tgz", + "integrity": "sha512-VzJUUH6cVz8yrb25RvvjhxCpwu4vUk28I0m5nnnhebULOo8p9lda5PgQeVde2+jQAd977C/vN714fkbYOmwb+A==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.54.0", + "@opentelemetry/semantic-conventions": "^1.27.0", + "@types/memcached": "^2.2.6" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-memcached/node_modules/@opentelemetry/api-logs": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.54.2.tgz", + "integrity": "sha512-4MTVwwmLgUh5QrJnZpYo6YRO5IBLAggf2h8gWDblwRagDStY13aEvt7gGk3jewrMaPlHiF83fENhIx0HO97/cQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation-memcached/node_modules/@opentelemetry/instrumentation": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.54.2.tgz", + "integrity": "sha512-go6zpOVoZVztT9r1aPd79Fr3OWiD4N24bCPJsIKkBses8oyFo12F/Ew3UBTdIu6hsW4HC4MVEJygG6TEyJI/lg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.54.2", + "@types/shimmer": "^1.2.0", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-mongodb": { + "version": "0.48.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongodb/-/instrumentation-mongodb-0.48.0.tgz", + "integrity": "sha512-9YWvaGvrrcrydMsYGLu0w+RgmosLMKe3kv/UNlsPy8RLnCkN2z+bhhbjjjuxtUmvEuKZMCoXFluABVuBr1yhjw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.54.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-mongodb/node_modules/@opentelemetry/api-logs": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.54.2.tgz", + "integrity": "sha512-4MTVwwmLgUh5QrJnZpYo6YRO5IBLAggf2h8gWDblwRagDStY13aEvt7gGk3jewrMaPlHiF83fENhIx0HO97/cQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation-mongodb/node_modules/@opentelemetry/instrumentation": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.54.2.tgz", + "integrity": "sha512-go6zpOVoZVztT9r1aPd79Fr3OWiD4N24bCPJsIKkBses8oyFo12F/Ew3UBTdIu6hsW4HC4MVEJygG6TEyJI/lg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.54.2", + "@types/shimmer": "^1.2.0", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-mongoose": { + "version": "0.43.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongoose/-/instrumentation-mongoose-0.43.0.tgz", + "integrity": "sha512-y1mWuL/zb6IKi199HkROgmStxF/ybEsnKYgx+/lpLATd57oZHOqrXP9tLmp9qRVI5c6P5XEWfe7ZCvrj07iDMQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.54.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-mongoose/node_modules/@opentelemetry/api-logs": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.54.2.tgz", + "integrity": "sha512-4MTVwwmLgUh5QrJnZpYo6YRO5IBLAggf2h8gWDblwRagDStY13aEvt7gGk3jewrMaPlHiF83fENhIx0HO97/cQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation-mongoose/node_modules/@opentelemetry/instrumentation": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.54.2.tgz", + "integrity": "sha512-go6zpOVoZVztT9r1aPd79Fr3OWiD4N24bCPJsIKkBses8oyFo12F/Ew3UBTdIu6hsW4HC4MVEJygG6TEyJI/lg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.54.2", + "@types/shimmer": "^1.2.0", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-mysql": { + "version": "0.42.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql/-/instrumentation-mysql-0.42.0.tgz", + "integrity": "sha512-1GN2EBGVSZABGQ25MSz3faeBW/DwhzmE10aNW1/A2mvQAxF1CvpMk17YmNUzwapVt29iKsiU3SXQG7vjh/019A==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.54.0", + "@opentelemetry/semantic-conventions": "^1.27.0", + "@types/mysql": "2.15.26" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-mysql/node_modules/@opentelemetry/api-logs": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.54.2.tgz", + "integrity": "sha512-4MTVwwmLgUh5QrJnZpYo6YRO5IBLAggf2h8gWDblwRagDStY13aEvt7gGk3jewrMaPlHiF83fENhIx0HO97/cQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation-mysql/node_modules/@opentelemetry/instrumentation": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.54.2.tgz", + "integrity": "sha512-go6zpOVoZVztT9r1aPd79Fr3OWiD4N24bCPJsIKkBses8oyFo12F/Ew3UBTdIu6hsW4HC4MVEJygG6TEyJI/lg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.54.2", + "@types/shimmer": "^1.2.0", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-mysql2": { + "version": "0.42.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql2/-/instrumentation-mysql2-0.42.1.tgz", + "integrity": "sha512-5hOQbFSpqsgDLaqIeWZNbSWB6XdwN+aBjoCIe60lmGG86zeNXu9I6l1kEckRb+Gy0i7zrt0Tk8S62zsOSZ8l7Q==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.54.0", + "@opentelemetry/semantic-conventions": "^1.27.0", + "@opentelemetry/sql-common": "^0.40.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-mysql2/node_modules/@opentelemetry/api-logs": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.54.2.tgz", + "integrity": "sha512-4MTVwwmLgUh5QrJnZpYo6YRO5IBLAggf2h8gWDblwRagDStY13aEvt7gGk3jewrMaPlHiF83fENhIx0HO97/cQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation-mysql2/node_modules/@opentelemetry/instrumentation": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.54.2.tgz", + "integrity": "sha512-go6zpOVoZVztT9r1aPd79Fr3OWiD4N24bCPJsIKkBses8oyFo12F/Ew3UBTdIu6hsW4HC4MVEJygG6TEyJI/lg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.54.2", + "@types/shimmer": "^1.2.0", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-nestjs-core": { + "version": "0.41.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-nestjs-core/-/instrumentation-nestjs-core-0.41.0.tgz", + "integrity": "sha512-XCqtghFktpcJ2BOaJtFfqtTMsHffJADxfYhJl28WT6ygCChS2uZVxMKKLsy+i9VtPaw/i1IumPICL6mbhwq+Vw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.54.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-nestjs-core/node_modules/@opentelemetry/api-logs": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.54.2.tgz", + "integrity": "sha512-4MTVwwmLgUh5QrJnZpYo6YRO5IBLAggf2h8gWDblwRagDStY13aEvt7gGk3jewrMaPlHiF83fENhIx0HO97/cQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation-nestjs-core/node_modules/@opentelemetry/instrumentation": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.54.2.tgz", + "integrity": "sha512-go6zpOVoZVztT9r1aPd79Fr3OWiD4N24bCPJsIKkBses8oyFo12F/Ew3UBTdIu6hsW4HC4MVEJygG6TEyJI/lg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.54.2", + "@types/shimmer": "^1.2.0", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-net": { + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-net/-/instrumentation-net-0.40.0.tgz", + "integrity": "sha512-abErnVRxTmtiF7EvBISW81Se2nj/j3Xtpfy//9++dgvDOXwbcD1Xz1via6ZHOm/VamboGhqPlYiO7ABzluPLwg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.54.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-net/node_modules/@opentelemetry/api-logs": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.54.2.tgz", + "integrity": "sha512-4MTVwwmLgUh5QrJnZpYo6YRO5IBLAggf2h8gWDblwRagDStY13aEvt7gGk3jewrMaPlHiF83fENhIx0HO97/cQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation-net/node_modules/@opentelemetry/instrumentation": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.54.2.tgz", + "integrity": "sha512-go6zpOVoZVztT9r1aPd79Fr3OWiD4N24bCPJsIKkBses8oyFo12F/Ew3UBTdIu6hsW4HC4MVEJygG6TEyJI/lg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.54.2", + "@types/shimmer": "^1.2.0", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-pg": { + "version": "0.47.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pg/-/instrumentation-pg-0.47.1.tgz", + "integrity": "sha512-qIcydMBVlKtAyFQWYunjqvFMVqIGvxGMXISrdLuSbcCqico9QKhK7bF5wzsotjGwHcGnc7q5kRqSL7j+LnY1Cw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^1.26.0", + "@opentelemetry/instrumentation": "^0.54.0", + "@opentelemetry/semantic-conventions": "1.27.0", + "@opentelemetry/sql-common": "^0.40.1", + "@types/pg": "8.6.1", + "@types/pg-pool": "2.0.6" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-pg/node_modules/@opentelemetry/api-logs": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.54.2.tgz", + "integrity": "sha512-4MTVwwmLgUh5QrJnZpYo6YRO5IBLAggf2h8gWDblwRagDStY13aEvt7gGk3jewrMaPlHiF83fENhIx0HO97/cQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation-pg/node_modules/@opentelemetry/instrumentation": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.54.2.tgz", + "integrity": "sha512-go6zpOVoZVztT9r1aPd79Fr3OWiD4N24bCPJsIKkBses8oyFo12F/Ew3UBTdIu6hsW4HC4MVEJygG6TEyJI/lg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.54.2", + "@types/shimmer": "^1.2.0", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-pg/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.27.0.tgz", + "integrity": "sha512-sAay1RrB+ONOem0OZanAR1ZI/k7yDpnOQSQmTMuGImUQb2y8EbSaCJ94FQluM74xoU03vlb2d2U90hZluL6nQg==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation-pino": { + "version": "0.43.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pino/-/instrumentation-pino-0.43.0.tgz", + "integrity": "sha512-jlOOgbODWRRNknWXY1VLgmqgG0SO4kLgU3XnejjO/3De4OisroAsMGk+1cRB5AQ6WZ8WLAMkMyTShaOe6j2Asw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "^0.54.0", + "@opentelemetry/core": "^1.25.0", + "@opentelemetry/instrumentation": "^0.54.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-pino/node_modules/@opentelemetry/api-logs": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.54.2.tgz", + "integrity": "sha512-4MTVwwmLgUh5QrJnZpYo6YRO5IBLAggf2h8gWDblwRagDStY13aEvt7gGk3jewrMaPlHiF83fENhIx0HO97/cQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation-pino/node_modules/@opentelemetry/instrumentation": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.54.2.tgz", + "integrity": "sha512-go6zpOVoZVztT9r1aPd79Fr3OWiD4N24bCPJsIKkBses8oyFo12F/Ew3UBTdIu6hsW4HC4MVEJygG6TEyJI/lg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.54.2", + "@types/shimmer": "^1.2.0", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-redis": { + "version": "0.43.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-redis/-/instrumentation-redis-0.43.0.tgz", + "integrity": "sha512-dufe08W3sCOjutbTJmV6tg2Y3+7IBe59oQrnIW2RCgjRhsW0Jjaenezt490eawO0MdXjUfFyrIUg8WetKhE4xA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.54.0", + "@opentelemetry/redis-common": "^0.36.2", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-redis-4": { + "version": "0.43.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-redis-4/-/instrumentation-redis-4-0.43.0.tgz", + "integrity": "sha512-6B2+CFRY9xRnkeZrSvlTyY2yB/zAgxjbXS5EwXhE3ZAKR1hWWoUzaTADIKT5xe9/VbDW42U3UoOPCcaCmeAXww==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.54.0", + "@opentelemetry/redis-common": "^0.36.2", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-redis-4/node_modules/@opentelemetry/api-logs": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.54.2.tgz", + "integrity": "sha512-4MTVwwmLgUh5QrJnZpYo6YRO5IBLAggf2h8gWDblwRagDStY13aEvt7gGk3jewrMaPlHiF83fENhIx0HO97/cQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation-redis-4/node_modules/@opentelemetry/instrumentation": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.54.2.tgz", + "integrity": "sha512-go6zpOVoZVztT9r1aPd79Fr3OWiD4N24bCPJsIKkBses8oyFo12F/Ew3UBTdIu6hsW4HC4MVEJygG6TEyJI/lg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.54.2", + "@types/shimmer": "^1.2.0", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-redis/node_modules/@opentelemetry/api-logs": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.54.2.tgz", + "integrity": "sha512-4MTVwwmLgUh5QrJnZpYo6YRO5IBLAggf2h8gWDblwRagDStY13aEvt7gGk3jewrMaPlHiF83fENhIx0HO97/cQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation-redis/node_modules/@opentelemetry/instrumentation": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.54.2.tgz", + "integrity": "sha512-go6zpOVoZVztT9r1aPd79Fr3OWiD4N24bCPJsIKkBses8oyFo12F/Ew3UBTdIu6hsW4HC4MVEJygG6TEyJI/lg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.54.2", + "@types/shimmer": "^1.2.0", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-restify": { + "version": "0.42.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-restify/-/instrumentation-restify-0.42.0.tgz", + "integrity": "sha512-ApDD9HNy6de6xrHmISEfkQHwwX1f1JrBj0ADnlk6tVdJ0j/vNmsZNLwaU2IA2K3mHqbp2YLarLgxAZp6rjcfWg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.54.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-restify/node_modules/@opentelemetry/api-logs": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.54.2.tgz", + "integrity": "sha512-4MTVwwmLgUh5QrJnZpYo6YRO5IBLAggf2h8gWDblwRagDStY13aEvt7gGk3jewrMaPlHiF83fENhIx0HO97/cQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation-restify/node_modules/@opentelemetry/instrumentation": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.54.2.tgz", + "integrity": "sha512-go6zpOVoZVztT9r1aPd79Fr3OWiD4N24bCPJsIKkBses8oyFo12F/Ew3UBTdIu6hsW4HC4MVEJygG6TEyJI/lg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.54.2", + "@types/shimmer": "^1.2.0", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-router": { + "version": "0.41.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-router/-/instrumentation-router-0.41.0.tgz", + "integrity": "sha512-IbvzgaoylMqStOOtwucEvSu5CDbfQN+H1ZZ2p6c9Kmvzptqh6G441GFy0FFVVqxOAHNhQm2w6n0Ag8trdBjCfw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.54.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-router/node_modules/@opentelemetry/api-logs": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.54.2.tgz", + "integrity": "sha512-4MTVwwmLgUh5QrJnZpYo6YRO5IBLAggf2h8gWDblwRagDStY13aEvt7gGk3jewrMaPlHiF83fENhIx0HO97/cQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation-router/node_modules/@opentelemetry/instrumentation": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.54.2.tgz", + "integrity": "sha512-go6zpOVoZVztT9r1aPd79Fr3OWiD4N24bCPJsIKkBses8oyFo12F/Ew3UBTdIu6hsW4HC4MVEJygG6TEyJI/lg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.54.2", + "@types/shimmer": "^1.2.0", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-socket.io": { + "version": "0.43.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-socket.io/-/instrumentation-socket.io-0.43.0.tgz", + "integrity": "sha512-HAQoIZ6N/ey1L4jF69gmqo7RyeSv5rc4sZZAd1v6SVaB8ZolTEyWEzGlu1NRZZTnqfWNxDkX6J1/omWpDd9k0w==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.54.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-socket.io/node_modules/@opentelemetry/api-logs": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.54.2.tgz", + "integrity": "sha512-4MTVwwmLgUh5QrJnZpYo6YRO5IBLAggf2h8gWDblwRagDStY13aEvt7gGk3jewrMaPlHiF83fENhIx0HO97/cQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation-socket.io/node_modules/@opentelemetry/instrumentation": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.54.2.tgz", + "integrity": "sha512-go6zpOVoZVztT9r1aPd79Fr3OWiD4N24bCPJsIKkBses8oyFo12F/Ew3UBTdIu6hsW4HC4MVEJygG6TEyJI/lg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.54.2", + "@types/shimmer": "^1.2.0", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-tedious": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-tedious/-/instrumentation-tedious-0.15.0.tgz", + "integrity": "sha512-Kb7yo8Zsq2TUwBbmwYgTAMPK0VbhoS8ikJ6Bup9KrDtCx2JC01nCb+M0VJWXt7tl0+5jARUbKWh5jRSoImxdCw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.54.0", + "@opentelemetry/semantic-conventions": "^1.27.0", + "@types/tedious": "^4.0.14" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-tedious/node_modules/@opentelemetry/api-logs": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.54.2.tgz", + "integrity": "sha512-4MTVwwmLgUh5QrJnZpYo6YRO5IBLAggf2h8gWDblwRagDStY13aEvt7gGk3jewrMaPlHiF83fENhIx0HO97/cQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation-tedious/node_modules/@opentelemetry/instrumentation": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.54.2.tgz", + "integrity": "sha512-go6zpOVoZVztT9r1aPd79Fr3OWiD4N24bCPJsIKkBses8oyFo12F/Ew3UBTdIu6hsW4HC4MVEJygG6TEyJI/lg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.54.2", + "@types/shimmer": "^1.2.0", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-undici": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-undici/-/instrumentation-undici-0.7.1.tgz", + "integrity": "sha512-sIl4zrRDP7pR+2Pmdm9XJQULMKiUmvZze2cEW6gUz7TXCEaYmJ+vNMdd7qgeRo8C7AMm+T08mptobFVKPzdz+A==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.54.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.7.0" + } + }, + "node_modules/@opentelemetry/instrumentation-undici/node_modules/@opentelemetry/api-logs": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.54.2.tgz", + "integrity": "sha512-4MTVwwmLgUh5QrJnZpYo6YRO5IBLAggf2h8gWDblwRagDStY13aEvt7gGk3jewrMaPlHiF83fENhIx0HO97/cQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation-undici/node_modules/@opentelemetry/instrumentation": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.54.2.tgz", + "integrity": "sha512-go6zpOVoZVztT9r1aPd79Fr3OWiD4N24bCPJsIKkBses8oyFo12F/Ew3UBTdIu6hsW4HC4MVEJygG6TEyJI/lg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.54.2", + "@types/shimmer": "^1.2.0", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-winston": { + "version": "0.41.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-winston/-/instrumentation-winston-0.41.0.tgz", + "integrity": "sha512-qtqGDx2Plu71s9xaeXut0YgZFG/y68ENG9vvo/SODeEC+4/APiS/htQ5YNJIxxjOuxYowdFYRqV9Kmef2EUzmw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "^0.54.0", + "@opentelemetry/instrumentation": "^0.54.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-winston/node_modules/@opentelemetry/api-logs": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.54.2.tgz", + "integrity": "sha512-4MTVwwmLgUh5QrJnZpYo6YRO5IBLAggf2h8gWDblwRagDStY13aEvt7gGk3jewrMaPlHiF83fENhIx0HO97/cQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation-winston/node_modules/@opentelemetry/instrumentation": { + "version": "0.54.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.54.2.tgz", + "integrity": "sha512-go6zpOVoZVztT9r1aPd79Fr3OWiD4N24bCPJsIKkBses8oyFo12F/Ew3UBTdIu6hsW4HC4MVEJygG6TEyJI/lg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.54.2", + "@types/shimmer": "^1.2.0", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/otlp-exporter-base": { + "version": "0.57.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.57.1.tgz", + "integrity": "sha512-GNBJAEYfeiYJQ3O2dvXgiNZ/qjWrBxSb1L1s7iV/jKBRGMN3Nv+miTk2SLeEobF5E5ZK4rVcHKlBZ71bPVIv/g==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/otlp-transformer": "0.57.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/otlp-grpc-exporter-base": { + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-grpc-exporter-base/-/otlp-grpc-exporter-base-0.55.0.tgz", + "integrity": "sha512-gebbjl9FiSp52igWXuGjcWQKfB6IBwFGt5z1VFwTcVZVeEZevB6bJIqoFrhH4A02m7OUlpJ7l4EfRi3UtkNANQ==", + "license": "Apache-2.0", + "dependencies": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "1.28.0", + "@opentelemetry/otlp-exporter-base": "0.55.0", + "@opentelemetry/otlp-transformer": "0.55.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/otlp-grpc-exporter-base/node_modules/@opentelemetry/api-logs": { + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.55.0.tgz", + "integrity": "sha512-3cpa+qI45VHYcA5c0bHM6VHo9gicv3p5mlLHNG3rLyjQU8b7e0st1rWtrUn3JbZ3DwwCfhKop4eQ9UuYlC6Pkg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/otlp-grpc-exporter-base/node_modules/@opentelemetry/core": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.28.0.tgz", + "integrity": "sha512-ZLwRMV+fNDpVmF2WYUdBHlq0eOWtEaUJSusrzjGnBt7iSRvfjFE3RXYUZJrqou/wIDWV0DwQ5KIfYe9WXg9Xqw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-grpc-exporter-base/node_modules/@opentelemetry/otlp-exporter-base": { + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.55.0.tgz", + "integrity": "sha512-iHQI0Zzq3h1T6xUJTVFwmFl5Dt5y1es+fl4kM+k5T/3YvmVyeYkSiF+wHCg6oKrlUAJfk+t55kaAu3sYmt7ZYA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.28.0", + "@opentelemetry/otlp-transformer": "0.55.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/otlp-grpc-exporter-base/node_modules/@opentelemetry/otlp-transformer": { + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.55.0.tgz", + "integrity": "sha512-kVqEfxtp6mSN2Dhpy0REo1ghP4PYhC1kMHQJ2qVlO99Pc+aigELjZDfg7/YKmL71gR6wVGIeJfiql/eXL7sQPA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.55.0", + "@opentelemetry/core": "1.28.0", + "@opentelemetry/resources": "1.28.0", + "@opentelemetry/sdk-logs": "0.55.0", + "@opentelemetry/sdk-metrics": "1.28.0", + "@opentelemetry/sdk-trace-base": "1.28.0", + "protobufjs": "^7.3.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/otlp-grpc-exporter-base/node_modules/@opentelemetry/resources": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.28.0.tgz", + "integrity": "sha512-cIyXSVJjGeTICENN40YSvLDAq4Y2502hGK3iN7tfdynQLKWb3XWZQEkPc+eSx47kiy11YeFAlYkEfXwR1w8kfw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.28.0", + "@opentelemetry/semantic-conventions": "1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-grpc-exporter-base/node_modules/@opentelemetry/sdk-logs": { + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.55.0.tgz", + "integrity": "sha512-TSx+Yg/d48uWW6HtjS1AD5x6WPfLhDWLl/WxC7I2fMevaiBuKCuraxTB8MDXieCNnBI24bw9ytyXrDCswFfWgA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.55.0", + "@opentelemetry/core": "1.28.0", + "@opentelemetry/resources": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.4.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-grpc-exporter-base/node_modules/@opentelemetry/sdk-metrics": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.28.0.tgz", + "integrity": "sha512-43tqMK/0BcKTyOvm15/WQ3HLr0Vu/ucAl/D84NO7iSlv6O4eOprxSHa3sUtmYkaZWHqdDJV0AHVz/R6u4JALVQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.28.0", + "@opentelemetry/resources": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-grpc-exporter-base/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.28.0.tgz", + "integrity": "sha512-ceUVWuCpIao7Y5xE02Xs3nQi0tOGmMea17ecBdwtCvdo9ekmO+ijc9RFDgfifMl7XCBf41zne/1POM3LqSTZDA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.28.0", + "@opentelemetry/resources": "1.28.0", + "@opentelemetry/semantic-conventions": "1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-grpc-exporter-base/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.27.0.tgz", + "integrity": "sha512-sAay1RrB+ONOem0OZanAR1ZI/k7yDpnOQSQmTMuGImUQb2y8EbSaCJ94FQluM74xoU03vlb2d2U90hZluL6nQg==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/otlp-transformer": { + "version": "0.57.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.57.1.tgz", + "integrity": "sha512-EX67y+ukNNfFrOLyjYGw8AMy0JPIlEX1dW60SGUNZWW2hSQyyolX7EqFuHP5LtXLjJHNfzx5SMBVQ3owaQCNDw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.57.1", + "@opentelemetry/core": "1.30.1", + "@opentelemetry/resources": "1.30.1", + "@opentelemetry/sdk-logs": "0.57.1", + "@opentelemetry/sdk-metrics": "1.30.1", + "@opentelemetry/sdk-trace-base": "1.30.1", + "protobufjs": "^7.3.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/propagation-utils": { + "version": "0.30.15", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagation-utils/-/propagation-utils-0.30.15.tgz", + "integrity": "sha512-nQ30K+eXTkd9Kt8yep9FPrqogS712GvdkV6R1T+xZMSZnFrRCyZuWxMtP3+s3hrK2HWw3ti4lsIfBzsHWYiyrA==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/propagator-b3": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-1.30.1.tgz", + "integrity": "sha512-oATwWWDIJzybAZ4pO76ATN5N6FFbOA1otibAVlS8v90B4S1wClnhRUk7K+2CHAwN1JKYuj4jh/lpCEG5BAqFuQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.30.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-jaeger": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-1.30.1.tgz", + "integrity": "sha512-Pj/BfnYEKIOImirH76M4hDaBSx6HyZ2CXUqk+Kj02m6BB80c/yo4BdWkn/1gDFfU+YPY+bPR2U0DKBfdxCKwmg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.30.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/redis-common": { + "version": "0.36.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/redis-common/-/redis-common-0.36.2.tgz", + "integrity": "sha512-faYX1N0gpLhej/6nyp6bgRjzAKXn5GOEMYY7YhciSfCoITAktLUtQ36d24QEWNA1/WA1y6qQunCe0OhHRkVl9g==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/resource-detector-alibaba-cloud": { + "version": "0.29.7", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-alibaba-cloud/-/resource-detector-alibaba-cloud-0.29.7.tgz", + "integrity": "sha512-PExUl/R+reSQI6Y/eNtgAsk6RHk1ElYSzOa8/FHfdc/nLmx9sqMasBEpLMkETkzDP7t27ORuXe4F9vwkV2uwwg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^1.26.0", + "@opentelemetry/resources": "^1.10.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/resource-detector-aws": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-aws/-/resource-detector-aws-1.11.0.tgz", + "integrity": "sha512-j7qQ75enAJrlSPkPowasScuukZ2ffFG659rhxOpUM4dBe/O8Jpq+dy4pIdFtjWKkM9i7LgisdUt/GW7wGIWoEQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^1.0.0", + "@opentelemetry/resources": "^1.10.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/resource-detector-azure": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-azure/-/resource-detector-azure-0.2.12.tgz", + "integrity": "sha512-iIarQu6MiCjEEp8dOzmBvCSlRITPFTinFB2oNKAjU6xhx8d7eUcjNOKhBGQTvuCriZrxrEvDaEEY9NfrPQ6uYQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^1.25.1", + "@opentelemetry/resources": "^1.10.1", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/resource-detector-container": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-container/-/resource-detector-container-0.5.3.tgz", + "integrity": "sha512-x5DxWu+ZALBuFpxwO2viv9ktH4Y3Gk9LaYKn2U8J+aeD412iy/OcGLPbQ76Px7pQ8qaJ5rnjcevBOHYT4aA+zQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^1.26.0", + "@opentelemetry/resources": "^1.10.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/resource-detector-gcp": { + "version": "0.29.13", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-gcp/-/resource-detector-gcp-0.29.13.tgz", + "integrity": "sha512-vdotx+l3Q+89PeyXMgKEGnZ/CwzwMtuMi/ddgD9/5tKZ08DfDGB2Npz9m2oXPHRCjc4Ro6ifMqFlRyzIvgOjhg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^1.0.0", + "@opentelemetry/resources": "^1.10.0", + "@opentelemetry/semantic-conventions": "^1.27.0", + "gcp-metadata": "^6.0.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/resources": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.30.1.tgz", + "integrity": "sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/resources/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", + "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/sdk-logs": { + "version": "0.57.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.57.1.tgz", + "integrity": "sha512-jGdObb/BGWu6Peo3cL3skx/Rl1Ak/wDDO3vpPrrThGbqE7isvkCsX6uE+OAt8Ayjm9YC8UGkohWbLR09JmM0FA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.57.1", + "@opentelemetry/core": "1.30.1", + "@opentelemetry/resources": "1.30.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.4.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-metrics": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.30.1.tgz", + "integrity": "sha512-q9zcZ0Okl8jRgmy7eNW3Ku1XSgg3sDLa5evHZpCwjspw7E8Is4K/haRPDJrBcX3YSn/Y7gUvFnByNYEKQNbNog==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/resources": "1.30.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node": { + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-node/-/sdk-node-0.55.0.tgz", + "integrity": "sha512-gSXQWV23+9vhbjsvAIeM0LxY3W8DTKI3MZlzFp61noIb1jSr46ET+qoUjHlfZ1Yymebv9KXWeZsqhft81HBXuQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.55.0", + "@opentelemetry/core": "1.28.0", + "@opentelemetry/exporter-logs-otlp-grpc": "0.55.0", + "@opentelemetry/exporter-logs-otlp-http": "0.55.0", + "@opentelemetry/exporter-logs-otlp-proto": "0.55.0", + "@opentelemetry/exporter-trace-otlp-grpc": "0.55.0", + "@opentelemetry/exporter-trace-otlp-http": "0.55.0", + "@opentelemetry/exporter-trace-otlp-proto": "0.55.0", + "@opentelemetry/exporter-zipkin": "1.28.0", + "@opentelemetry/instrumentation": "0.55.0", + "@opentelemetry/resources": "1.28.0", + "@opentelemetry/sdk-logs": "0.55.0", + "@opentelemetry/sdk-metrics": "1.28.0", + "@opentelemetry/sdk-trace-base": "1.28.0", + "@opentelemetry/sdk-trace-node": "1.28.0", + "@opentelemetry/semantic-conventions": "1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/api-logs": { + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.55.0.tgz", + "integrity": "sha512-3cpa+qI45VHYcA5c0bHM6VHo9gicv3p5mlLHNG3rLyjQU8b7e0st1rWtrUn3JbZ3DwwCfhKop4eQ9UuYlC6Pkg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/context-async-hooks": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.28.0.tgz", + "integrity": "sha512-igcl4Ve+F1N2063PJUkesk/GkYyuGIWinYkSyAFTnIj3gzrOgvOA4k747XNdL47HRRL1w/qh7UW8NDuxOLvKFA==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/core": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.28.0.tgz", + "integrity": "sha512-ZLwRMV+fNDpVmF2WYUdBHlq0eOWtEaUJSusrzjGnBt7iSRvfjFE3RXYUZJrqou/wIDWV0DwQ5KIfYe9WXg9Xqw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/exporter-trace-otlp-http": { + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.55.0.tgz", + "integrity": "sha512-lMiNic63EVHpW+eChmLD2CieDmwQBFi72+LFbh8+5hY0ShrDGrsGP/zuT5MRh7M/vM/UZYO/2A/FYd7CMQGR7A==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.28.0", + "@opentelemetry/otlp-exporter-base": "0.55.0", + "@opentelemetry/otlp-transformer": "0.55.0", + "@opentelemetry/resources": "1.28.0", + "@opentelemetry/sdk-trace-base": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/exporter-zipkin": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-zipkin/-/exporter-zipkin-1.28.0.tgz", + "integrity": "sha512-AMwr3eGXaPEH7gk8yhcUcen31VXy1yU5VJETu0pCfGpggGCYmhm0FKgYBpL5/vlIgQJWU/sW2vIjCL7aSilpKg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.28.0", + "@opentelemetry/resources": "1.28.0", + "@opentelemetry/sdk-trace-base": "1.28.0", + "@opentelemetry/semantic-conventions": "1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/instrumentation": { + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.55.0.tgz", + "integrity": "sha512-YDCMlaQRZkziLL3t6TONRgmmGxDx6MyQDXRD0dknkkgUZtOK5+8MWft1OXzmNu6XfBOdT12MKN5rz+jHUkafKQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.55.0", + "@types/shimmer": "^1.2.0", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/otlp-exporter-base": { + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.55.0.tgz", + "integrity": "sha512-iHQI0Zzq3h1T6xUJTVFwmFl5Dt5y1es+fl4kM+k5T/3YvmVyeYkSiF+wHCg6oKrlUAJfk+t55kaAu3sYmt7ZYA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.28.0", + "@opentelemetry/otlp-transformer": "0.55.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/otlp-transformer": { + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.55.0.tgz", + "integrity": "sha512-kVqEfxtp6mSN2Dhpy0REo1ghP4PYhC1kMHQJ2qVlO99Pc+aigELjZDfg7/YKmL71gR6wVGIeJfiql/eXL7sQPA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.55.0", + "@opentelemetry/core": "1.28.0", + "@opentelemetry/resources": "1.28.0", + "@opentelemetry/sdk-logs": "0.55.0", + "@opentelemetry/sdk-metrics": "1.28.0", + "@opentelemetry/sdk-trace-base": "1.28.0", + "protobufjs": "^7.3.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/propagator-b3": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-1.28.0.tgz", + "integrity": "sha512-Q7HVDIMwhN5RxL4bECMT4BdbyYSAKkC6U/RGn4NpO/cbqP6ZRg+BS7fPo/pGZi2w8AHfpIGQFXQmE8d2PC5xxQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/propagator-jaeger": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-1.28.0.tgz", + "integrity": "sha512-wKJ94+s8467CnIRgoSRh0yXm/te0QMOwTq9J01PfG/RzYZvlvN8aRisN2oZ9SznB45dDGnMj3BhUlchSA9cEKA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/resources": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.28.0.tgz", + "integrity": "sha512-cIyXSVJjGeTICENN40YSvLDAq4Y2502hGK3iN7tfdynQLKWb3XWZQEkPc+eSx47kiy11YeFAlYkEfXwR1w8kfw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.28.0", + "@opentelemetry/semantic-conventions": "1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/sdk-logs": { + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.55.0.tgz", + "integrity": "sha512-TSx+Yg/d48uWW6HtjS1AD5x6WPfLhDWLl/WxC7I2fMevaiBuKCuraxTB8MDXieCNnBI24bw9ytyXrDCswFfWgA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.55.0", + "@opentelemetry/core": "1.28.0", + "@opentelemetry/resources": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.4.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/sdk-metrics": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.28.0.tgz", + "integrity": "sha512-43tqMK/0BcKTyOvm15/WQ3HLr0Vu/ucAl/D84NO7iSlv6O4eOprxSHa3sUtmYkaZWHqdDJV0AHVz/R6u4JALVQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.28.0", + "@opentelemetry/resources": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.28.0.tgz", + "integrity": "sha512-ceUVWuCpIao7Y5xE02Xs3nQi0tOGmMea17ecBdwtCvdo9ekmO+ijc9RFDgfifMl7XCBf41zne/1POM3LqSTZDA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.28.0", + "@opentelemetry/resources": "1.28.0", + "@opentelemetry/semantic-conventions": "1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/sdk-trace-node": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-1.28.0.tgz", + "integrity": "sha512-N0sYfYXvHpP0FNIyc+UfhLnLSTOuZLytV0qQVrDWIlABeD/DWJIGttS7nYeR14gQLXch0M1DW8zm3VeN6Opwtg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/context-async-hooks": "1.28.0", + "@opentelemetry/core": "1.28.0", + "@opentelemetry/propagator-b3": "1.28.0", + "@opentelemetry/propagator-jaeger": "1.28.0", + "@opentelemetry/sdk-trace-base": "1.28.0", + "semver": "^7.5.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.27.0.tgz", + "integrity": "sha512-sAay1RrB+ONOem0OZanAR1ZI/k7yDpnOQSQmTMuGImUQb2y8EbSaCJ94FQluM74xoU03vlb2d2U90hZluL6nQg==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.30.1.tgz", + "integrity": "sha512-jVPgBbH1gCy2Lb7X0AVQ8XAfgg0pJ4nvl8/IiQA6nxOsPvS+0zMJaFSs2ltXe0J6C8dqjcnpyqINDJmU30+uOg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/resources": "1.30.1", + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-base/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", + "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/sdk-trace-node": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-1.30.1.tgz", + "integrity": "sha512-cBjYOINt1JxXdpw1e5MlHmFRc5fgj4GW/86vsKFxJCJ8AL4PdVtYH41gWwl4qd4uQjqEL1oJVrXkSy5cnduAnQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/context-async-hooks": "1.30.1", + "@opentelemetry/core": "1.30.1", + "@opentelemetry/propagator-b3": "1.30.1", + "@opentelemetry/propagator-jaeger": "1.30.1", + "@opentelemetry/sdk-trace-base": "1.30.1", + "semver": "^7.5.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/semantic-conventions": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.29.0.tgz", + "integrity": "sha512-KZ1JsXcP2pqunfsJBNk+py6AJ5R6ZJ3yvM5Lhhf93rHPHvdDzgfMYPS4F7GNO3j/MVDCtfbttrkcpu7sl0Wu/Q==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/sql-common": { + "version": "0.40.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sql-common/-/sql-common-0.40.1.tgz", + "integrity": "sha512-nSDlnHSqzC3pXn/wZEZVLuAuJ1MYMXPBwtv2qAbCa3847SaHItdE7SzUq/Jtb0KZmh1zfAbNi3AAMjztTT4Ugg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^1.1.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@polka/url": { + "version": "1.0.0-next.28", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.28.tgz", + "integrity": "sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", + "license": "BSD-3-Clause" + }, + "node_modules/@purista/amqpbridge": { + "resolved": "packages/amqpbridge", + "link": true + }, + "node_modules/@purista/aws-config-store": { + "resolved": "packages/aws-config-store", + "link": true + }, + "node_modules/@purista/aws-secret-store": { + "resolved": "packages/aws-secret-store", + "link": true + }, + "node_modules/@purista/azure-secret-store": { + "resolved": "packages/azure-secret-store", + "link": true + }, + "node_modules/@purista/base-http-bridge": { + "resolved": "packages/base-http-bridge", + "link": true + }, + "node_modules/@purista/cli": { + "resolved": "packages/cli", + "link": true + }, + "node_modules/@purista/core": { + "resolved": "packages/core", + "link": true + }, + "node_modules/@purista/dapr-example": { + "resolved": "examples/dapr-example", + "link": true + }, + "node_modules/@purista/dapr-sdk": { + "resolved": "packages/dapr-sdk", + "link": true + }, + "node_modules/@purista/full-example": { + "resolved": "examples/fullexample", + "link": true + }, + "node_modules/@purista/gcloud-secret-store": { + "resolved": "packages/gcloud-secret-store", + "link": true + }, + "node_modules/@purista/hono-example": { + "resolved": "examples/hono-example", + "link": true + }, + "node_modules/@purista/hono-http-server": { + "resolved": "packages/hono-http-server", + "link": true + }, + "node_modules/@purista/httpserver": { + "resolved": "packages/httpserver", + "link": true + }, + "node_modules/@purista/infisical-secret-store": { + "resolved": "packages/infisical-secret-store", + "link": true + }, + "node_modules/@purista/k8s-sdk": { + "resolved": "packages/k8s-sdk", + "link": true + }, + "node_modules/@purista/kubernetes-example": { + "resolved": "examples/kubernetes", + "link": true + }, + "node_modules/@purista/mqtt-example": { + "resolved": "examples/mqtt-bridge", + "link": true + }, + "node_modules/@purista/mqttbridge": { + "resolved": "packages/mqttbridge", + "link": true + }, + "node_modules/@purista/nats-config-store": { + "resolved": "packages/nats-config-store", + "link": true + }, + "node_modules/@purista/nats-example": { + "resolved": "examples/nats-bridge", + "link": true + }, + "node_modules/@purista/nats-state-store": { + "resolved": "packages/nats-state-store", + "link": true + }, + "node_modules/@purista/natsbridge": { + "resolved": "packages/natsbridge", + "link": true + }, + "node_modules/@purista/quickstart": { + "resolved": "examples/quickstart", + "link": true + }, + "node_modules/@purista/redis-config-store": { + "resolved": "packages/redis-config-store", + "link": true + }, + "node_modules/@purista/redis-state-store": { + "resolved": "packages/redis-state-store", + "link": true + }, + "node_modules/@purista/temporal-example": { + "resolved": "examples/temporal", + "link": true + }, + "node_modules/@purista/website": { + "resolved": "website", + "link": true + }, + "node_modules/@redis/client": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.6.0.tgz", + "integrity": "sha512-aR0uffYI700OEEH4gYnitAnv3vzVGXCFvYfdpu/CJKvk4pHfLPEy/JSZyrpQ+15WhXe1yJRXLtfQ84s4mEXnPg==", + "license": "MIT", + "dependencies": { + "cluster-key-slot": "1.1.2", + "generic-pool": "3.9.0", + "yallist": "4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.6.tgz", + "integrity": "sha512-+GcCXtOQoWuC7hhX1P00LqjjIiS/iOouHXhMdiDSnq/1DGTox4SpUvO52Xm+div6+106r+TcvOeo/cxvyEyTgg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.34.6.tgz", + "integrity": "sha512-E8+2qCIjciYUnCa1AiVF1BkRgqIGW9KzJeesQqVfyRITGQN+dFuoivO0hnro1DjT74wXLRZ7QF8MIbz+luGaJA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.34.6.tgz", + "integrity": "sha512-z9Ib+OzqN3DZEjX7PDQMHEhtF+t6Mi2z/ueChQPLS/qUMKY7Ybn5A2ggFoKRNRh1q1T03YTQfBTQCJZiepESAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.34.6.tgz", + "integrity": "sha512-PShKVY4u0FDAR7jskyFIYVyHEPCPnIQY8s5OcXkdU8mz3Y7eXDJPdyM/ZWjkYdR2m0izD9HHWA8sGcXn+Qrsyg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.34.6.tgz", + "integrity": "sha512-YSwyOqlDAdKqs0iKuqvRHLN4SrD2TiswfoLfvYXseKbL47ht1grQpq46MSiQAx6rQEN8o8URtpXARCpqabqxGQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.34.6.tgz", + "integrity": "sha512-HEP4CgPAY1RxXwwL5sPFv6BBM3tVeLnshF03HMhJYCNc6kvSqBgTMmsEjb72RkZBAWIqiPUyF1JpEBv5XT9wKQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.34.6.tgz", + "integrity": "sha512-88fSzjC5xeH9S2Vg3rPgXJULkHcLYMkh8faix8DX4h4TIAL65ekwuQMA/g2CXq8W+NJC43V6fUpYZNjaX3+IIg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.34.6.tgz", + "integrity": "sha512-wM4ztnutBqYFyvNeR7Av+reWI/enK9tDOTKNF+6Kk2Q96k9bwhDDOlnCUNRPvromlVXo04riSliMBs/Z7RteEg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.34.6.tgz", + "integrity": "sha512-9RyprECbRa9zEjXLtvvshhw4CMrRa3K+0wcp3KME0zmBe1ILmvcVHnypZ/aIDXpRyfhSYSuN4EPdCCj5Du8FIA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.34.6.tgz", + "integrity": "sha512-qTmklhCTyaJSB05S+iSovfo++EwnIEZxHkzv5dep4qoszUMX5Ca4WM4zAVUMbfdviLgCSQOu5oU8YoGk1s6M9Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.34.6.tgz", + "integrity": "sha512-4Qmkaps9yqmpjY5pvpkfOerYgKNUGzQpFxV6rnS7c/JfYbDSU0y6WpbbredB5cCpLFGJEqYX40WUmxMkwhWCjw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.34.6.tgz", + "integrity": "sha512-Zsrtux3PuaxuBTX/zHdLaFmcofWGzaWW1scwLU3ZbW/X+hSsFbz9wDIp6XvnT7pzYRl9MezWqEqKy7ssmDEnuQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.34.6.tgz", + "integrity": "sha512-aK+Zp+CRM55iPrlyKiU3/zyhgzWBxLVrw2mwiQSYJRobCURb781+XstzvA8Gkjg/hbdQFuDw44aUOxVQFycrAg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.34.6.tgz", + "integrity": "sha512-WoKLVrY9ogmaYPXwTH326+ErlCIgMmsoRSx6bO+l68YgJnlOXhygDYSZe/qbUJCSiCiZAQ+tKm88NcWuUXqOzw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.34.6.tgz", + "integrity": "sha512-Sht4aFvmA4ToHd2vFzwMFaQCiYm2lDFho5rPcvPBT5pCdC+GwHG6CMch4GQfmWTQ1SwRKS0dhDYb54khSrjDWw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.34.6.tgz", + "integrity": "sha512-zmmpOQh8vXc2QITsnCiODCDGXFC8LMi64+/oPpPx5qz3pqv0s6x46ps4xoycfUiVZps5PFn1gksZzo4RGTKT+A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.34.6.tgz", + "integrity": "sha512-3/q1qUsO/tLqGBaD4uXsB6coVGB3usxw3qyeVb59aArCgedSF66MPdgRStUd7vbZOsko/CgVaY5fo2vkvPLWiA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.34.6.tgz", + "integrity": "sha512-oLHxuyywc6efdKVTxvc0135zPrRdtYVjtVD5GUm55I3ODxhU/PwkQFD97z16Xzxa1Fz0AEe4W/2hzRtd+IfpOA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.6.tgz", + "integrity": "sha512-0PVwmgzZ8+TZ9oGBmdZoQVXflbvuwzN/HRclujpl4N/q3i+y0lqLw8n1bXA8ru3sApDjlmONaNAuYr38y1Kr9w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@scalar/hono-api-reference": { + "version": "0.5.172", + "resolved": "https://registry.npmjs.org/@scalar/hono-api-reference/-/hono-api-reference-0.5.172.tgz", + "integrity": "sha512-2fd7PidgQWSzP8IKjApK16aN0lDtcV2muigBO/AIdMUzEowRlDFt6nhqrCDkFerX/6mCqmTewFczo731cL+kHg==", + "license": "MIT", + "dependencies": { + "@scalar/types": "0.0.31" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "hono": "^4.0.0" + } + }, + "node_modules/@scalar/openapi-types": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/@scalar/openapi-types/-/openapi-types-0.1.7.tgz", + "integrity": "sha512-oOTG3JQifg55U3DhKB7WdNIxFnJzbPJe7rqdyWdio977l8IkxQTVmObftJhdNIMvhV2K+1f/bDoMQGu6yTaD0A==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@scalar/types": { + "version": "0.0.31", + "resolved": "https://registry.npmjs.org/@scalar/types/-/types-0.0.31.tgz", + "integrity": "sha512-xagRDDqqf+tMTtChUggQHIzjMbfHe/3ntiZpF4RDgrU5yA3v/eYFwLjb+OgfcMpU1c02FSUeKa6FN7e7DZcXgg==", + "license": "MIT", + "dependencies": { + "@scalar/openapi-types": "0.1.7", + "@unhead/schema": "^1.11.11" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@scarf/scarf": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@scarf/scarf/-/scarf-1.4.0.tgz", + "integrity": "sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==", + "hasInstallScript": true, + "license": "Apache-2.0" + }, + "node_modules/@shikijs/core": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-2.3.2.tgz", + "integrity": "sha512-s7vyL3LzUKm3Qwf36zRWlavX9BQMZTIq9B1almM63M5xBuSldnsTHCmsXzoF/Kyw4k7Xgas7yAyJz9VR/vcP1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/engine-javascript": "2.3.2", + "@shikijs/engine-oniguruma": "2.3.2", + "@shikijs/types": "2.3.2", + "@shikijs/vscode-textmate": "^10.0.1", + "@types/hast": "^3.0.4", + "hast-util-to-html": "^9.0.4" + } + }, + "node_modules/@shikijs/core/node_modules/@shikijs/engine-oniguruma": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-2.3.2.tgz", + "integrity": "sha512-vikMY1TroyZXUHIXbMnvY/mjtOxMn+tavcfAeQPgWS9FHcgFSUoEtywF5B5sOLb9NXb8P2vb7odkh3nj15/00A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "2.3.2", + "@shikijs/vscode-textmate": "^10.0.1" + } + }, + "node_modules/@shikijs/core/node_modules/@shikijs/types": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-2.3.2.tgz", + "integrity": "sha512-CBaMY+a3pepyC4SETi7+bSzO0f6hxEQJUUuS4uD7zppzjmrN4ZRtBqxaT+wOan26CR9eeJ5iBhc4qvWEwn7Eeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/vscode-textmate": "^10.0.1", + "@types/hast": "^3.0.4" + } + }, + "node_modules/@shikijs/engine-javascript": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-2.3.2.tgz", + "integrity": "sha512-w3IEMu5HfL/OaJTsMbIfZ1HRPnWVYRANeDtmsdIIEgUOcLjzFJFQwlnkckGjKHekEzNqlMLbgB/twnfZ/EEAGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "2.3.2", + "@shikijs/vscode-textmate": "^10.0.1", + "oniguruma-to-es": "^3.1.0" + } + }, + "node_modules/@shikijs/engine-javascript/node_modules/@shikijs/types": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-2.3.2.tgz", + "integrity": "sha512-CBaMY+a3pepyC4SETi7+bSzO0f6hxEQJUUuS4uD7zppzjmrN4ZRtBqxaT+wOan26CR9eeJ5iBhc4qvWEwn7Eeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/vscode-textmate": "^10.0.1", + "@types/hast": "^3.0.4" + } + }, + "node_modules/@shikijs/engine-oniguruma": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-1.29.2.tgz", + "integrity": "sha512-7iiOx3SG8+g1MnlzZVDYiaeHe7Ez2Kf2HrJzdmGwkRisT7r4rak0e655AcM/tF9JG/kg5fMNYlLLKglbN7gBqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "1.29.2", + "@shikijs/vscode-textmate": "^10.0.1" + } + }, + "node_modules/@shikijs/langs": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-2.3.2.tgz", + "integrity": "sha512-UqI6bSxFzhexIJficZLKeB1L2Sc3xoNiAV0yHpfbg5meck93du+EKQtsGbBv66Ki53XZPhnR/kYkOr85elIuFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "2.3.2" + } + }, + "node_modules/@shikijs/langs/node_modules/@shikijs/types": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-2.3.2.tgz", + "integrity": "sha512-CBaMY+a3pepyC4SETi7+bSzO0f6hxEQJUUuS4uD7zppzjmrN4ZRtBqxaT+wOan26CR9eeJ5iBhc4qvWEwn7Eeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/vscode-textmate": "^10.0.1", + "@types/hast": "^3.0.4" + } + }, + "node_modules/@shikijs/themes": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-2.3.2.tgz", + "integrity": "sha512-QAh7D/hhfYKHibkG2tti8vxNt3ekAH5EqkXJeJbTh7FGvTCWEI7BHqNCtMdjFvZ0vav5nvUgdvA7/HI7pfsB4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "2.3.2" + } + }, + "node_modules/@shikijs/themes/node_modules/@shikijs/types": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-2.3.2.tgz", + "integrity": "sha512-CBaMY+a3pepyC4SETi7+bSzO0f6hxEQJUUuS4uD7zppzjmrN4ZRtBqxaT+wOan26CR9eeJ5iBhc4qvWEwn7Eeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/vscode-textmate": "^10.0.1", + "@types/hast": "^3.0.4" + } + }, + "node_modules/@shikijs/transformers": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@shikijs/transformers/-/transformers-2.3.2.tgz", + "integrity": "sha512-2HDnJumw8A/9GecRpTgvfqSbPjEbJ4DPWq5J++OVP1gNMLvbV0MqFsP4canqRNM1LqB7VmWY45Stipb0ZIJ+0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/core": "2.3.2", + "@shikijs/types": "2.3.2" + } + }, + "node_modules/@shikijs/transformers/node_modules/@shikijs/types": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-2.3.2.tgz", + "integrity": "sha512-CBaMY+a3pepyC4SETi7+bSzO0f6hxEQJUUuS4uD7zppzjmrN4ZRtBqxaT+wOan26CR9eeJ5iBhc4qvWEwn7Eeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/vscode-textmate": "^10.0.1", + "@types/hast": "^3.0.4" + } + }, + "node_modules/@shikijs/types": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-1.29.2.tgz", + "integrity": "sha512-VJjK0eIijTZf0QSTODEXCqinjBn0joAHQ+aPSBzrv4O2d/QSbsMw+ZeSRx03kV34Hy7NzUvV/7NqfYGRLrASmw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/vscode-textmate": "^10.0.1", + "@types/hast": "^3.0.4" + } + }, + "node_modules/@shikijs/vscode-textmate": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.1.tgz", + "integrity": "sha512-fTIQwLF+Qhuws31iw7Ncl1R3HUDtGwIipiJ9iU+UsDUwMhegFcQKQHd51nZjb7CArq0MvON8rbgCGQYWHUKAdg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "13.0.5", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.5.tgz", + "integrity": "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.1" + } + }, + "node_modules/@sinonjs/samsam": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.2.tgz", + "integrity": "sha512-v46t/fwnhejRSFTGqbpn9u+LQ9xJDse10gNnPgAcxgdoCDMXj/G2asWAC/8Qs+BAZDicX+MNZouXT1A7c83kVw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.1", + "lodash.get": "^4.4.2", + "type-detect": "^4.1.0" + } + }, + "node_modules/@sinonjs/samsam/node_modules/type-detect": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", + "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@sinonjs/text-encoding": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.3.tgz", + "integrity": "sha512-DE427ROAphMQzU4ENbliGYrBSYPXF+TtLg9S8vzeA+OF4ZKzoDdzfL8sxuMUGS/lgRhM6j1URSk9ghf7Xo1tyA==", + "dev": true, + "license": "(Unlicense OR Apache-2.0)" + }, + "node_modules/@smithy/abort-controller": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.0.1.tgz", + "integrity": "sha512-fiUIYgIgRjMWznk6iLJz35K2YxSLHzLBA/RC6lBrKfQ8fHbPfvk7Pk9UvpKoHgJjI18MnbPuEju53zcVy6KF1g==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/config-resolver": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.0.1.tgz", + "integrity": "sha512-Igfg8lKu3dRVkTSEm98QpZUvKEOa71jDX4vKRcvJVyRc3UgN3j7vFMf0s7xLQhYmKa8kyJGQgUJDOV5V3neVlQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-middleware": "^4.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/core": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.1.2.tgz", + "integrity": "sha512-htwQXkbdF13uwwDevz9BEzL5ABK+1sJpVQXywwGSH973AVOvisHNfpcB8A8761G6XgHoS2kHPqc9DqHJ2gp+/Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/middleware-serde": "^4.0.2", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-stream": "^4.0.2", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/credential-provider-imds": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.0.1.tgz", + "integrity": "sha512-l/qdInaDq1Zpznpmev/+52QomsJNZ3JkTl5yrTl02V6NBgJOQ4LY0SFw/8zsMwj3tLe8vqiIuwF6nxaEwgf6mg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.0.1", + "@smithy/property-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/url-parser": "^4.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/fetch-http-handler": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.0.1.tgz", + "integrity": "sha512-3aS+fP28urrMW2KTjb6z9iFow6jO8n3MFfineGbndvzGZit3taZhKWtTorf+Gp5RpFDDafeHlhfsGlDCXvUnJA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.0.1", + "@smithy/querystring-builder": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-base64": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/hash-node": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.0.1.tgz", + "integrity": "sha512-TJ6oZS+3r2Xu4emVse1YPB3Dq3d8RkZDKcPr71Nj/lJsdAP1c7oFzYqEn1IBc915TsgLl2xIJNuxCz+gLbLE0w==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/invalid-dependency": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.0.1.tgz", + "integrity": "sha512-gdudFPf4QRQ5pzj7HEnu6FhKRi61BfH/Gk5Yf6O0KiSbr1LlVhgjThcvjdu658VE6Nve8vaIWB8/fodmS1rBPQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/is-array-buffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.0.0.tgz", + "integrity": "sha512-saYhF8ZZNoJDTvJBEWgeBccCg+yvp1CX+ed12yORU3NilJScfc6gfch2oVb4QgxZrGUx3/ZJlb+c/dJbyupxlw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-content-length": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.0.1.tgz", + "integrity": "sha512-OGXo7w5EkB5pPiac7KNzVtfCW2vKBTZNuCctn++TTSOMpe6RZO/n6WEC1AxJINn3+vWLKW49uad3lo/u0WJ9oQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-endpoint": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.0.3.tgz", + "integrity": "sha512-YdbmWhQF5kIxZjWqPIgboVfi8i5XgiYMM7GGKFMTvBei4XjNQfNv8sukT50ITvgnWKKKpOtp0C0h7qixLgb77Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.1.2", + "@smithy/middleware-serde": "^4.0.2", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/url-parser": "^4.0.1", + "@smithy/util-middleware": "^4.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-retry": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.0.4.tgz", + "integrity": "sha512-wmxyUBGHaYUqul0wZiset4M39SMtDBOtUr2KpDuftKNN74Do9Y36Go6Eqzj9tL0mIPpr31ulB5UUtxcsCeGXsQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/service-error-classification": "^4.0.1", + "@smithy/smithy-client": "^4.1.3", + "@smithy/types": "^4.1.0", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-retry": "^4.0.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-serde": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.0.2.tgz", + "integrity": "sha512-Sdr5lOagCn5tt+zKsaW+U2/iwr6bI9p08wOkCp6/eL6iMbgdtc2R5Ety66rf87PeohR0ExI84Txz9GYv5ou3iQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-stack": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.0.1.tgz", + "integrity": "sha512-dHwDmrtR/ln8UTHpaIavRSzeIk5+YZTBtLnKwDW3G2t6nAupCiQUvNzNoHBpik63fwUaJPtlnMzXbQrNFWssIA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/node-config-provider": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.0.1.tgz", + "integrity": "sha512-8mRTjvCtVET8+rxvmzRNRR0hH2JjV0DFOmwXPrISmTIJEfnCBugpYYGAsCj8t41qd+RB5gbheSQ/6aKZCQvFLQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/node-http-handler": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.0.2.tgz", + "integrity": "sha512-X66H9aah9hisLLSnGuzRYba6vckuFtGE+a5DcHLliI/YlqKrGoxhisD5XbX44KyoeRzoNlGr94eTsMVHFAzPOw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/querystring-builder": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/property-provider": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.0.1.tgz", + "integrity": "sha512-o+VRiwC2cgmk/WFV0jaETGOtX16VNPp2bSQEzu0whbReqE1BMqsP2ami2Vi3cbGVdKu1kq9gQkDAGKbt0WOHAQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/protocol-http": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.0.1.tgz", + "integrity": "sha512-TE4cpj49jJNB/oHyh/cRVEgNZaoPaxd4vteJNB0yGidOCVR0jCw/hjPVsT8Q8FRmj8Bd3bFZt8Dh7xGCT+xMBQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/querystring-builder": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.0.1.tgz", + "integrity": "sha512-wU87iWZoCbcqrwszsOewEIuq+SU2mSoBE2CcsLwE0I19m0B2gOJr1MVjxWcDQYOzHbR1xCk7AcOBbGFUYOKvdg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "@smithy/util-uri-escape": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/querystring-parser": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.0.1.tgz", + "integrity": "sha512-Ma2XC7VS9aV77+clSFylVUnPZRindhB7BbmYiNOdr+CHt/kZNJoPP0cd3QxCnCFyPXC4eybmyE98phEHkqZ5Jw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/service-error-classification": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.0.1.tgz", + "integrity": "sha512-3JNjBfOWpj/mYfjXJHB4Txc/7E4LVq32bwzE7m28GN79+M1f76XHflUaSUkhOriprPDzev9cX/M+dEB80DNDKA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/shared-ini-file-loader": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.0.1.tgz", + "integrity": "sha512-hC8F6qTBbuHRI/uqDgqqi6J0R4GtEZcgrZPhFQnMhfJs3MnUTGSnR1NSJCJs5VWlMydu0kJz15M640fJlRsIOw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/signature-v4": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.0.1.tgz", + "integrity": "sha512-nCe6fQ+ppm1bQuw5iKoeJ0MJfz2os7Ic3GBjOkLOPtavbD1ONoyE3ygjBfz2ythFWm4YnRm6OxW+8p/m9uCoIA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^4.0.0", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-hex-encoding": "^4.0.0", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-uri-escape": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/smithy-client": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.1.3.tgz", + "integrity": "sha512-A2Hz85pu8BJJaYFdX8yb1yocqigyqBzn+OVaVgm+Kwi/DkN8vhN2kbDVEfADo6jXf5hPKquMLGA3UINA64UZ7A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.1.2", + "@smithy/middleware-endpoint": "^4.0.3", + "@smithy/middleware-stack": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-stream": "^4.0.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/types": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.1.0.tgz", + "integrity": "sha512-enhjdwp4D7CXmwLtD6zbcDMbo6/T6WtuuKCY49Xxc6OMOmUWlBEBDREsxxgV2LIdeQPW756+f97GzcgAwp3iLw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/url-parser": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.0.1.tgz", + "integrity": "sha512-gPXcIEUtw7VlK8f/QcruNXm7q+T5hhvGu9tl63LsJPZ27exB6dtNwvh2HIi0v7JcXJ5emBxB+CJxwaLEdJfA+g==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/querystring-parser": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-base64": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.0.0.tgz", + "integrity": "sha512-CvHfCmO2mchox9kjrtzoHkWHxjHZzaFojLc8quxXY7WAAMAg43nuxwv95tATVgQFNDwd4M9S1qFzj40Ul41Kmg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-body-length-browser": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.0.0.tgz", + "integrity": "sha512-sNi3DL0/k64/LO3A256M+m3CDdG6V7WKWHdAiBBMUN8S3hK3aMPhwnPik2A/a2ONN+9doY9UxaLfgqsIRg69QA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-body-length-node": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.0.0.tgz", + "integrity": "sha512-q0iDP3VsZzqJyje8xJWEJCNIu3lktUGVoSy1KB0UWym2CL1siV3artm+u1DFYTLejpsrdGyCSWBdGNjJzfDPjg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-buffer-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.0.0.tgz", + "integrity": "sha512-9TOQ7781sZvddgO8nxueKi3+yGvkY35kotA0Y6BWRajAv8jjmigQ1sBwz0UX47pQMYXJPahSKEKYFgt+rXdcug==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-config-provider": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.0.0.tgz", + "integrity": "sha512-L1RBVzLyfE8OXH+1hsJ8p+acNUSirQnWQ6/EgpchV88G6zGBTDPdXiiExei6Z1wR2RxYvxY/XLw6AMNCCt8H3w==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-browser": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.0.4.tgz", + "integrity": "sha512-Ej1bV5sbrIfH++KnWxjjzFNq9nyP3RIUq2c9Iqq7SmMO/idUR24sqvKH2LUQFTSPy/K7G4sB2m8n7YYlEAfZaw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^4.0.1", + "@smithy/smithy-client": "^4.1.3", + "@smithy/types": "^4.1.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-node": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.0.4.tgz", + "integrity": "sha512-HE1I7gxa6yP7ZgXPCFfZSDmVmMtY7SHqzFF55gM/GPegzZKaQWZZ+nYn9C2Cc3JltCMyWe63VPR3tSFDEvuGjw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/config-resolver": "^4.0.1", + "@smithy/credential-provider-imds": "^4.0.1", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/property-provider": "^4.0.1", + "@smithy/smithy-client": "^4.1.3", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-endpoints": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.0.1.tgz", + "integrity": "sha512-zVdUENQpdtn9jbpD9SCFK4+aSiavRb9BxEtw9ZGUR1TYo6bBHbIoi7VkrFQ0/RwZlzx0wRBaRmPclj8iAoJCLA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-hex-encoding": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.0.0.tgz", + "integrity": "sha512-Yk5mLhHtfIgW2W2WQZWSg5kuMZCVbvhFmC7rV4IO2QqnZdbEFPmQnCcGMAX2z/8Qj3B9hYYNjZOhWym+RwhePw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-middleware": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.0.1.tgz", + "integrity": "sha512-HiLAvlcqhbzhuiOa0Lyct5IIlyIz0PQO5dnMlmQ/ubYM46dPInB+3yQGkfxsk6Q24Y0n3/JmcA1v5iEhmOF5mA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-retry": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.0.1.tgz", + "integrity": "sha512-WmRHqNVwn3kI3rKk1LsKcVgPBG6iLTBGC1iYOV3GQegwJ3E8yjzHytPt26VNzOWr1qu0xE03nK0Ug8S7T7oufw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/service-error-classification": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-stream": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.0.2.tgz", + "integrity": "sha512-0eZ4G5fRzIoewtHtwaYyl8g2C+osYOT4KClXgfdNEDAgkbe2TYPqcnw4GAWabqkZCax2ihRGPe9LZnsPdIUIHA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/fetch-http-handler": "^5.0.1", + "@smithy/node-http-handler": "^4.0.2", + "@smithy/types": "^4.1.0", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-hex-encoding": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-uri-escape": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.0.0.tgz", + "integrity": "sha512-77yfbCbQMtgtTylO9itEAdpPXSog3ZxMe09AEhm0dU0NLTalV70ghDZFR+Nfi1C60jnJoh/Re4090/DuZh2Omg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-utf8": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.0.0.tgz", + "integrity": "sha512-b+zebfKCfRdgNJDknHCob3O7FpeYQN6ZG6YLExMcasDHsCXlsXCEuiPZeLnJLpwa5dvPetGlnGCiMHuLwGvFow==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-waiter": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.0.2.tgz", + "integrity": "sha512-piUTHyp2Axx3p/kc2CIJkYSv0BAaheBQmbACZgQSSfWUumWNW+R1lL+H9PDBxKJkvOeEX+hKYEFiwO8xagL8AQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@sodaru/yup-to-json-schema": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@sodaru/yup-to-json-schema/-/yup-to-json-schema-2.0.1.tgz", + "integrity": "sha512-lWb0Wiz8KZ9ip/dY1eUqt7fhTPmL24p6Hmv5Fd9pzlzAdw/YNcWZr+tiCT4oZ4Zyxzi9+1X4zv82o7jYvcFxYA==", + "dev": true, + "license": "MIT License" + }, + "node_modules/@swc/core": { + "version": "1.10.15", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.10.15.tgz", + "integrity": "sha512-/iFeQuNaGdK7mfJbQcObhAhsMqLT7qgMYl7jX2GEIO+VDTejESpzAyKwaMeYXExN8D6e5BRHBCe7M5YlsuzjDA==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@swc/counter": "^0.1.3", + "@swc/types": "^0.1.17" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/swc" + }, + "optionalDependencies": { + "@swc/core-darwin-arm64": "1.10.15", + "@swc/core-darwin-x64": "1.10.15", + "@swc/core-linux-arm-gnueabihf": "1.10.15", + "@swc/core-linux-arm64-gnu": "1.10.15", + "@swc/core-linux-arm64-musl": "1.10.15", + "@swc/core-linux-x64-gnu": "1.10.15", + "@swc/core-linux-x64-musl": "1.10.15", + "@swc/core-win32-arm64-msvc": "1.10.15", + "@swc/core-win32-ia32-msvc": "1.10.15", + "@swc/core-win32-x64-msvc": "1.10.15" + }, + "peerDependencies": { + "@swc/helpers": "*" + }, + "peerDependenciesMeta": { + "@swc/helpers": { + "optional": true + } + } + }, + "node_modules/@swc/core-darwin-arm64": { + "version": "1.10.15", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.10.15.tgz", + "integrity": "sha512-zFdZ6/yHqMCPk7OhLFqHy/MQ1EqJhcZMpNHd1gXYT7VRU3FaqvvKETrUlG3VYl65McPC7AhMRfXPyJ0JO/jARQ==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-darwin-x64": { + "version": "1.10.15", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.10.15.tgz", + "integrity": "sha512-8g4yiQwbr8fxOOjKXdot0dEkE5zgE8uNZudLy/ZyAhiwiZ8pbJ8/wVrDOu6dqbX7FBXAoDnvZ7fwN1jk4C8jdA==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm-gnueabihf": { + "version": "1.10.15", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.10.15.tgz", + "integrity": "sha512-rl+eVOltl2+7WXOnvmWBpMgh6aO13G5x0U0g8hjwlmD6ku3Y9iRcThpOhm7IytMEarUp5pQxItNoPq+VUGjVHg==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-gnu": { + "version": "1.10.15", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.10.15.tgz", + "integrity": "sha512-qxWEQeyAJMWJqjaN4hi58WMpPdt3Tn0biSK9CYRegQtvZWCbewr6v2agtSu5AZ2rudeH6OfCWAMDQQeSgn6PJQ==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-musl": { + "version": "1.10.15", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.10.15.tgz", + "integrity": "sha512-QcELd9/+HjZx0WCxRrKcyKGWTiQ0485kFb5w8waxcSNd0d9Lgk4EFfWWVyvIb5gIHpDQmhrgzI/yRaWQX4YSZQ==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-gnu": { + "version": "1.10.15", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.10.15.tgz", + "integrity": "sha512-S1+ZEEn3+a/MiMeQqQypbwTGoBG8/sPoCvpNbk+uValyygT+jSn3U0xVr45FbukpmMB+NhBMqfedMLqKA0QnJA==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-musl": { + "version": "1.10.15", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.10.15.tgz", + "integrity": "sha512-qW+H9g/2zTJ4jP7NDw4VAALY0ZlNEKzYsEoSj/HKi7k3tYEHjMzsxjfsY9I8WZCft23bBdV3RTCPoxCshaj1CQ==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-arm64-msvc": { + "version": "1.10.15", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.10.15.tgz", + "integrity": "sha512-AhRB11aA6LxjIqut+mg7qsu/7soQDmbK6MKR9nP3hgBszpqtXbRba58lr24xIbBCMr+dpo6kgEapWt+t5Po6Zg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-ia32-msvc": { + "version": "1.10.15", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.10.15.tgz", + "integrity": "sha512-UGdh430TQwbDn6KjgvRTg1fO022sbQ4yCCHUev0+5B8uoBwi9a89qAz3emy2m56C8TXxUoihW9Y9OMfaRwPXUw==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-x64-msvc": { + "version": "1.10.15", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.10.15.tgz", + "integrity": "sha512-XJzBCqO1m929qbJsOG7FZXQWX26TnEoMctS3QjuCoyBmkHxxQmZsy78KjMes1aomTcKHCyFYgrRGWgVmk7tT4Q==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", + "license": "Apache-2.0" + }, + "node_modules/@swc/types": { + "version": "0.1.17", + "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.17.tgz", + "integrity": "sha512-V5gRru+aD8YVyCOMAjMpWR1Ui577DD5KSJsHP8RAxopAH22jFz6GZd/qxqjO6MJHQhcsjvjOFXyDhyLQUnMveQ==", + "license": "Apache-2.0", + "dependencies": { + "@swc/counter": "^0.1.3" + } + }, + "node_modules/@temporalio/activity": { + "version": "1.11.7", + "resolved": "https://registry.npmjs.org/@temporalio/activity/-/activity-1.11.7.tgz", + "integrity": "sha512-8WjXmvre0He9RvipP5nPK8dDbC/EoMtllCjcdiyPI69jDiihoTxOLpiYXIXdHn9yGbf34lHa21ZiRVZNhFkeHg==", + "license": "MIT", + "dependencies": { + "@temporalio/common": "1.11.7", + "abort-controller": "^3.0.0" + } + }, + "node_modules/@temporalio/client": { + "version": "1.11.7", + "resolved": "https://registry.npmjs.org/@temporalio/client/-/client-1.11.7.tgz", + "integrity": "sha512-rStMiKMNPH396TDBIDoavi2B/QQaV6NHHzEzhmyD7MoDJvuApIv1uSKcjYlRdmXiv0lF1FC2ONcquq7lmKSCYg==", + "license": "MIT", + "dependencies": { + "@grpc/grpc-js": "^1.10.7", + "@temporalio/common": "1.11.7", + "@temporalio/proto": "1.11.7", + "abort-controller": "^3.0.0", + "long": "^5.2.3", + "uuid": "^9.0.1" + } + }, + "node_modules/@temporalio/common": { + "version": "1.11.7", + "resolved": "https://registry.npmjs.org/@temporalio/common/-/common-1.11.7.tgz", + "integrity": "sha512-T3so7KE8nGmesjrbrfjtQLXhw7lJYKW87V/soxNeAxW/i3n3/7km3/dx8F7FBlgbu66Uu8CIWVbxLnVSS+Frbg==", + "license": "MIT", + "dependencies": { + "@temporalio/proto": "1.11.7", + "long": "^5.2.3", + "ms": "^3.0.0-canary.1", + "proto3-json-serializer": "^2.0.0" + } + }, + "node_modules/@temporalio/common/node_modules/ms": { + "version": "3.0.0-canary.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-3.0.0-canary.1.tgz", + "integrity": "sha512-kh8ARjh8rMN7Du2igDRO9QJnqCb2xYTJxyQYK7vJJS4TvLLmsbyhiKpSW+t+y26gyOyMd0riphX0GeWKU3ky5g==", + "license": "MIT", + "engines": { + "node": ">=12.13" + } + }, + "node_modules/@temporalio/core-bridge": { + "version": "1.11.7", + "resolved": "https://registry.npmjs.org/@temporalio/core-bridge/-/core-bridge-1.11.7.tgz", + "integrity": "sha512-QiVqX2b+7GiZ9VlyeUunDILMvgU2UX2PyWXemuoLeM+33X/xbLW/UgwtDS2TKcON69UrUH1OG9yl6DPXpeOrfQ==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@temporalio/common": "1.11.7", + "arg": "^5.0.2", + "cargo-cp-artifact": "^0.1.8", + "which": "^4.0.0" + } + }, + "node_modules/@temporalio/core-bridge/node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "license": "MIT" + }, + "node_modules/@temporalio/core-bridge/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/@temporalio/core-bridge/node_modules/which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^16.13.0 || >=18.0.0" + } + }, + "node_modules/@temporalio/interceptors-opentelemetry": { + "version": "1.11.7", + "resolved": "https://registry.npmjs.org/@temporalio/interceptors-opentelemetry/-/interceptors-opentelemetry-1.11.7.tgz", + "integrity": "sha512-q8Bpg+RQiLeV0bnEEMp5qSKk274cYoG631LatKqzxYgB/VxrnZd8gM4HDqYsCape3JS0bu68GYueOba+vOUSzA==", + "license": "MIT", + "dependencies": { + "@opentelemetry/api": "^1.7.0", + "@opentelemetry/core": "^1.19.0", + "@opentelemetry/resources": "^1.19.0", + "@opentelemetry/sdk-trace-base": "^1.19.0" + }, + "peerDependencies": { + "@temporalio/activity": "1.11.7", + "@temporalio/client": "1.11.7", + "@temporalio/common": "1.11.7", + "@temporalio/worker": "1.11.7", + "@temporalio/workflow": "1.11.7" + } + }, + "node_modules/@temporalio/proto": { + "version": "1.11.7", + "resolved": "https://registry.npmjs.org/@temporalio/proto/-/proto-1.11.7.tgz", + "integrity": "sha512-O34R2wk8PvmYpMHhsL7g2lIdtTA5wkQ5LJ86eHY0cF9zhDs/CoGiysl9qBOg9fy4WXHqzmcUm/V50hGefaM7gw==", + "license": "MIT", + "dependencies": { + "long": "^5.2.3", + "protobufjs": "^7.2.5" + } + }, + "node_modules/@temporalio/worker": { + "version": "1.11.7", + "resolved": "https://registry.npmjs.org/@temporalio/worker/-/worker-1.11.7.tgz", + "integrity": "sha512-r8AMBFFQkZghsVnqJ7RcPBBXYhm498oATL7FY9WL1kBf73wQ7cZl82Vug1VT9fHF/SX3Kq8WdNXC3jPhO+jG6g==", + "license": "MIT", + "dependencies": { + "@swc/core": "^1.3.102", + "@temporalio/activity": "1.11.7", + "@temporalio/client": "1.11.7", + "@temporalio/common": "1.11.7", + "@temporalio/core-bridge": "1.11.7", + "@temporalio/proto": "1.11.7", + "@temporalio/workflow": "1.11.7", + "abort-controller": "^3.0.0", + "heap-js": "^2.3.0", + "memfs": "^4.6.0", + "rxjs": "^7.8.1", + "source-map": "^0.7.4", + "source-map-loader": "^4.0.2", + "supports-color": "^8.1.1", + "swc-loader": "^0.2.3", + "unionfs": "^4.5.1", + "webpack": "^5.94.0" + }, + "engines": { + "node": ">= 16.0.0" + } + }, + "node_modules/@temporalio/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==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/@temporalio/workflow": { + "version": "1.11.7", + "resolved": "https://registry.npmjs.org/@temporalio/workflow/-/workflow-1.11.7.tgz", + "integrity": "sha512-NGNmGCoV3xPvRifjRhCZKbIbPAup92/OC2xuaTb3hzBb13N9Pbm6HVljgQORzKNyO55rXDRpTnAiJ40en57IgA==", + "license": "MIT", + "dependencies": { + "@temporalio/common": "1.11.7", + "@temporalio/proto": "1.11.7" + } + }, + "node_modules/@testcontainers/nats": { + "version": "10.18.0", + "resolved": "https://registry.npmjs.org/@testcontainers/nats/-/nats-10.18.0.tgz", + "integrity": "sha512-9URcraYb3WGSGE+1MzfNg9MwDUrMrWt/U4olF9LgA6IsSz1lKijoTXb0KP0yPWYSLrdKxD1mBcZpwur8kUxg1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "testcontainers": "^10.18.0" + } + }, + "node_modules/@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/@ts-morph/common": { + "version": "0.26.1", + "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.26.1.tgz", + "integrity": "sha512-Sn28TGl/4cFpcM+jwsH1wLncYq3FtN/BIpem+HOygfBWPT5pAeS5dB4VFVzV8FbnOKHpDLZmvAl4AjPEev5idA==", + "license": "MIT", + "dependencies": { + "fast-glob": "^3.3.2", + "minimatch": "^9.0.4", + "path-browserify": "^1.0.1" + } + }, + "node_modules/@ts-morph/common/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "dev": true, + "license": "MIT" + }, + "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, + "license": "MIT" + }, + "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, + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/amqplib": { + "version": "0.10.6", + "resolved": "https://registry.npmjs.org/@types/amqplib/-/amqplib-0.10.6.tgz", + "integrity": "sha512-vQLVypBS1JQcfTXhl1Td1EEeLdtb+vuulOb4TrzYiLyP2aYLMAEzB3pNmEA0jBm0xIXu946Y7Xwl19Eidl32SQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/aws-lambda": { + "version": "8.10.143", + "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.143.tgz", + "integrity": "sha512-u5vzlcR14ge/4pMTTMDQr3MF0wEe38B2F9o84uC4F43vN5DGTy63npRrB6jQhyt+C0lGv4ZfiRcRkqJoZuPnmg==", + "license": "MIT" + }, + "node_modules/@types/bunyan": { + "version": "1.8.9", + "resolved": "https://registry.npmjs.org/@types/bunyan/-/bunyan-1.8.9.tgz", + "integrity": "sha512-ZqS9JGpBxVOvsawzmVt30sP++gSQMTejCkIAQ3VdadOcRE8izTyW66hufvwLeH+YEGP6Js2AW7Gz+RMyvrEbmw==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/caseless": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.5.tgz", + "integrity": "sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg==", + "license": "MIT" + }, + "node_modules/@types/connect": { + "version": "3.4.36", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.36.tgz", + "integrity": "sha512-P63Zd/JUGq+PdrM1lv0Wv5SBYeA2+CORvbrXbngriYY0jzLUWfQMQQxOhjONEz/wlHOAxOdY7CY65rgQdTjq2w==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/docker-modem": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/docker-modem/-/docker-modem-3.0.6.tgz", + "integrity": "sha512-yKpAGEuKRSS8wwx0joknWxsmLha78wNMe9R2S3UNsVOkZded8UqOrV8KoeDXoXsjndxwyF3eIhyClGbO1SEhEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/ssh2": "*" + } + }, + "node_modules/@types/dockerode": { + "version": "3.3.34", + "resolved": "https://registry.npmjs.org/@types/dockerode/-/dockerode-3.3.34.tgz", + "integrity": "sha512-mH9SuIb8NuTDsMus5epcbTzSbEo52fKLBMo0zapzYIAIyfDqoIFn7L3trekHLKC8qmxGV++pPUP4YqQ9n5v2Zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/docker-modem": "*", + "@types/node": "*", + "@types/ssh2": "*" + } + }, + "node_modules/@types/eslint": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", + "license": "MIT", + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", + "license": "MIT", + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "license": "MIT" + }, + "node_modules/@types/fined": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@types/fined/-/fined-1.1.5.tgz", + "integrity": "sha512-2N93vadEGDFhASTIRbizbl4bNqpMOId5zZfj6hHqYZfEzEfO9onnU4Im8xvzo8uudySDveDHBOOSlTWf38ErfQ==", + "license": "MIT" + }, + "node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/inquirer": { + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/@types/inquirer/-/inquirer-9.0.7.tgz", + "integrity": "sha512-Q0zyBupO6NxGRZut/JdmqYKOnN95Eg5V8Csg3PGKkP+FnvsUZx1jAyK7fztIszxxMuoBA6E3KXWvdZVXIpx60g==", + "license": "MIT", + "dependencies": { + "@types/through": "*", + "rxjs": "^7.2.0" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "license": "MIT" + }, + "node_modules/@types/liftoff": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/liftoff/-/liftoff-4.0.3.tgz", + "integrity": "sha512-UgbL2kR5pLrWICvr8+fuSg0u43LY250q7ZMkC+XKC3E+rs/YBDEnQIzsnhU5dYsLlwMi3R75UvCL87pObP1sxw==", + "license": "MIT", + "dependencies": { + "@types/fined": "*", + "@types/node": "*" + } + }, + "node_modules/@types/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/long": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", + "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==", + "license": "MIT" + }, + "node_modules/@types/markdown-it": { + "version": "14.1.2", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", + "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/linkify-it": "^5", + "@types/mdurl": "^2" + } + }, + "node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/memcached": { + "version": "2.2.10", + "resolved": "https://registry.npmjs.org/@types/memcached/-/memcached-2.2.10.tgz", + "integrity": "sha512-AM9smvZN55Gzs2wRrqeMHVP7KE8KWgCJO/XL5yCly2xF6EKa4YlbpK+cLSAH4NG/Ah64HrlegmGqW8kYws7Vxg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/mysql": { + "version": "2.15.26", + "resolved": "https://registry.npmjs.org/@types/mysql/-/mysql-2.15.26.tgz", + "integrity": "sha512-DSLCOXhkvfS5WNNPbfn2KdICAmk8lLc+/PNvnPnF7gOdMZCxopXduqv0OQ13y/yA/zXTSikZZqVgybUxOEg6YQ==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "22.13.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.1.tgz", + "integrity": "sha512-jK8uzQlrvXqEU91UxiK5J7pKHyzgnI1Qnl0QDHIgVGuolJhRb9EEl28Cj9b3rGR8B2lhFCtvIm5os8lFnO/1Ew==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "node_modules/@types/pg": { + "version": "8.6.1", + "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.6.1.tgz", + "integrity": "sha512-1Kc4oAGzAl7uqUStZCDvaLFqZrW9qWSjXOmBfdgyBP5La7Us6Mg4GBvRlSoaZMhQF/zSj1C8CtKMBkoiT8eL8w==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "pg-protocol": "*", + "pg-types": "^2.2.0" + } + }, + "node_modules/@types/pg-pool": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/pg-pool/-/pg-pool-2.0.6.tgz", + "integrity": "sha512-TaAUE5rq2VQYxab5Ts7WZhKNmuN78Q6PiFonTDdpbx8a1H0M1vhy3rhiMjl+e2iHmogyMw7jZF4FrE6eJUy5HQ==", + "license": "MIT", + "dependencies": { + "@types/pg": "*" + } + }, + "node_modules/@types/qs": { + "version": "6.9.18", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz", + "integrity": "sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/readable-stream": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-4.0.18.tgz", + "integrity": "sha512-21jK/1j+Wg+7jVw1xnSwy/2Q1VgVjWuFssbYGTREPUBeZ+rqVFl2udq0IkxzPC0ZhOzVceUbyIACFZKLqKEBlA==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "safe-buffer": "~5.1.1" + } + }, + "node_modules/@types/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/@types/request": { + "version": "2.48.12", + "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.12.tgz", + "integrity": "sha512-G3sY+NpsA9jnwm0ixhAFQSJ3Q9JkpLZpJbI3GMv0mIAT0y3mRabYeINzal5WOChIiaTEGQYlHOKgkaM9EisWHw==", + "license": "MIT", + "dependencies": { + "@types/caseless": "*", + "@types/node": "*", + "@types/tough-cookie": "*", + "form-data": "^2.5.0" + } + }, + "node_modules/@types/shimmer": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@types/shimmer/-/shimmer-1.2.0.tgz", + "integrity": "sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg==", + "license": "MIT" + }, + "node_modules/@types/sinon": { + "version": "17.0.3", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-17.0.3.tgz", + "integrity": "sha512-j3uovdn8ewky9kRBG19bOwaZbexJu/XjtkHyjvUgt4xfPFz18dcORIMqnYh66Fx3Powhcr85NT5+er3+oViapw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@types/sinonjs__fake-timers": "*" + } + }, + "node_modules/@types/sinonjs__fake-timers": { + "version": "8.1.5", + "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.5.tgz", + "integrity": "sha512-mQkU2jY8jJEF7YHjHvsQO8+3ughTL1mcnn96igfhONmR+fUPSKIkefQYpSe8bsly2Ep7oQbn/6VG5/9/0qcArQ==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@types/ssh2": { + "version": "1.15.4", + "resolved": "https://registry.npmjs.org/@types/ssh2/-/ssh2-1.15.4.tgz", + "integrity": "sha512-9JTQgVBWSgq6mAen6PVnrAmty1lqgCMvpfN+1Ck5WRUsyMYPa6qd50/vMJ0y1zkGpOEgLzm8m8Dx/Y5vRouLaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "^18.11.18" + } + }, + "node_modules/@types/ssh2-streams": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/@types/ssh2-streams/-/ssh2-streams-0.1.12.tgz", + "integrity": "sha512-Sy8tpEmCce4Tq0oSOYdfqaBpA3hDM8SoxoFh5vzFsu2oL+znzGz8oVWW7xb4K920yYMUY+PIG31qZnFMfPWNCg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/ssh2/node_modules/@types/node": { + "version": "18.19.75", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.75.tgz", + "integrity": "sha512-UIksWtThob6ZVSyxcOqCLOUNg/dyO1Qvx4McgeuhrEtHTLFTf7BBhEazaE4K806FGTPtzd/2sE90qn4fVr7cyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/ssh2/node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/swagger-ui-dist": { + "version": "3.30.5", + "resolved": "https://registry.npmjs.org/@types/swagger-ui-dist/-/swagger-ui-dist-3.30.5.tgz", + "integrity": "sha512-SrXhD9L8qeIxJzN+o1kmf3wXeVf/+Km3jIdRM1+Yq3I5b/dlF5TcGr5WCVM7I/cBYpgf43/gCPIucQ13AhICiw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/tedious": { + "version": "4.0.14", + "resolved": "https://registry.npmjs.org/@types/tedious/-/tedious-4.0.14.tgz", + "integrity": "sha512-KHPsfX/FoVbUGbyYvk1q9MMQHLPeRZhRJZdO45Q4YjvFkv4hMNghCWTvy7rdKessBsmtz4euWCWAB6/tVpI1Iw==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/through": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/@types/through/-/through-0.0.33.tgz", + "integrity": "sha512-HsJ+z3QuETzP3cswwtzt2vEIiHBk/dCcHGhbmG5X3ecnwFD/lPrMpliGXxSCg03L9AhrdwA4Oz/qfspkDW+xGQ==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/tough-cookie": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", + "license": "MIT" + }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/uuid": { + "version": "9.0.8", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", + "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", + "license": "MIT" + }, + "node_modules/@types/web-bluetooth": { + "version": "0.0.20", + "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz", + "integrity": "sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/ws": { + "version": "8.5.14", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.14.tgz", + "integrity": "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@typeschema/core": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@typeschema/core/-/core-0.14.0.tgz", + "integrity": "sha512-Ia6PtZHcL3KqsAWXjMi5xIyZ7XMH4aSnOQes8mfMLx+wGFGtGRNlwe6Y7cYvX+WfNK67OL0/HSe9t8QDygV0/w==", + "license": "MIT", + "peerDependencies": { + "@types/json-schema": "^7.0.15" + }, + "peerDependenciesMeta": { + "@types/json-schema": { + "optional": true + } + } + }, + "node_modules/@typeschema/json": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@typeschema/json/-/json-0.14.0.tgz", + "integrity": "sha512-QU6hQzx9U3VKXSK0egNDanASGCE0CryjQVso3KdWldf1/rVAn5MRi6c3iyTdrayg3ZMETAT8wS7IXHVtcClo0Q==", + "license": "MIT", + "dependencies": { + "@typeschema/core": "0.14.0" + }, + "peerDependencies": { + "ajv": "^8.17.1", + "json-schema-to-ts": "^3.1.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + }, + "json-schema-to-ts": { + "optional": true + } + } + }, + "node_modules/@typeschema/main": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/@typeschema/main/-/main-0.14.1.tgz", + "integrity": "sha512-ReYIFVmMawLn3rq6+fBhiDFH1LrolQd+kKcuglCgnMip20ou2h2YEuIcDoLJ1tQ/RQnBTPyfvs+BwEytW9YVDg==", + "license": "MIT", + "dependencies": { + "@typeschema/core": "0.14.0" + }, + "peerDependencies": { + "@typeschema/arktype": "0.14.0", + "@typeschema/class-validator": "0.3.0", + "@typeschema/deepkit": "0.14.0", + "@typeschema/effect": "0.14.0", + "@typeschema/fastest-validator": "0.2.0", + "@typeschema/function": "0.14.0", + "@typeschema/io-ts": "0.14.0", + "@typeschema/joi": "0.14.0", + "@typeschema/json": "0.14.0", + "@typeschema/ow": "0.14.0", + "@typeschema/runtypes": "0.14.0", + "@typeschema/superstruct": "0.14.0", + "@typeschema/suretype": "0.2.0", + "@typeschema/typebox": "0.14.0", + "@typeschema/valibot": "0.14.0", + "@typeschema/valita": "0.2.0", + "@typeschema/vine": "0.2.0", + "@typeschema/yup": "0.14.0", + "@typeschema/zod": "0.14.0" + }, + "peerDependenciesMeta": { + "@typeschema/arktype": { + "optional": true + }, + "@typeschema/class-validator": { + "optional": true + }, + "@typeschema/deepkit": { + "optional": true + }, + "@typeschema/effect": { + "optional": true + }, + "@typeschema/fastest-validator": { + "optional": true + }, + "@typeschema/function": { + "optional": true + }, + "@typeschema/io-ts": { + "optional": true + }, + "@typeschema/joi": { + "optional": true + }, + "@typeschema/json": { + "optional": true + }, + "@typeschema/ow": { + "optional": true + }, + "@typeschema/runtypes": { + "optional": true + }, + "@typeschema/superstruct": { + "optional": true + }, + "@typeschema/suretype": { + "optional": true + }, + "@typeschema/typebox": { + "optional": true + }, + "@typeschema/valibot": { + "optional": true + }, + "@typeschema/valita": { + "optional": true + }, + "@typeschema/vine": { + "optional": true + }, + "@typeschema/yup": { + "optional": true + }, + "@typeschema/zod": { + "optional": true + } + } + }, + "node_modules/@typeschema/yup": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@typeschema/yup/-/yup-0.14.0.tgz", + "integrity": "sha512-KBki4UPDwnKKBtLDKGPLXZXUm6cV9+zLLWgbt2kSaZV4VonrDciuCq4h6P1opguEU2UsQiXuTcdVeqlTS3tA0g==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@typeschema/core": "0.14.0" + }, + "peerDependencies": { + "@sodaru/yup-to-json-schema": "^2.0.1", + "yup": "^1.4.0" + }, + "peerDependenciesMeta": { + "@sodaru/yup-to-json-schema": { + "optional": true + }, + "yup": { + "optional": true + } + } + }, + "node_modules/@typeschema/zod": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@typeschema/zod/-/zod-0.14.0.tgz", + "integrity": "sha512-oEX3lQFVKyGCmAV3KtVS290WpFukg/ZBXMOYpLFH/POlCaU8ojCJu5ehYvNDPZNK/f1KK9Vqlo28f7YX2g3O5w==", + "license": "MIT", + "dependencies": { + "@typeschema/core": "0.14.0" + }, + "peerDependencies": { + "zod": "^3.23.8", + "zod-to-json-schema": "^3.23.2" + }, + "peerDependenciesMeta": { + "zod": { + "optional": true + }, + "zod-to-json-schema": { + "optional": true + } + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "dev": true, + "license": "ISC" + }, + "node_modules/@unhead/schema": { + "version": "1.11.18", + "resolved": "https://registry.npmjs.org/@unhead/schema/-/schema-1.11.18.tgz", + "integrity": "sha512-a3TA/OJCRdfbFhcA3Hq24k1ZU1o9szicESrw8DZcGyQFacHnh84mVgnyqSkMnwgCmfN4kvjSiTBlLEHS6+wATw==", + "license": "MIT", + "dependencies": { + "hookable": "^5.5.3", + "zhead": "^2.2.4" + }, + "funding": { + "url": "https://github.com/sponsors/harlan-zw" + } + }, + "node_modules/@uptrace/core": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/@uptrace/core/-/core-1.22.0.tgz", + "integrity": "sha512-PHU57VgXp3PFeKT7NEeaYHFlf9U+xRyTXJuDidJQhjRlj7tIj3mNd3wKR3Lv2MDbrxKs82wbG3mKr9ptezLAaQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "~1.9.0", + "@opentelemetry/core": "~1.28.0", + "@opentelemetry/instrumentation": "~0.55.0", + "@opentelemetry/resources": "~1.28.0", + "@opentelemetry/sdk-trace-base": "~1.28.0", + "cross-fetch": "4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@uptrace/core/node_modules/@opentelemetry/api-logs": { + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.55.0.tgz", + "integrity": "sha512-3cpa+qI45VHYcA5c0bHM6VHo9gicv3p5mlLHNG3rLyjQU8b7e0st1rWtrUn3JbZ3DwwCfhKop4eQ9UuYlC6Pkg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@uptrace/core/node_modules/@opentelemetry/core": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.28.0.tgz", + "integrity": "sha512-ZLwRMV+fNDpVmF2WYUdBHlq0eOWtEaUJSusrzjGnBt7iSRvfjFE3RXYUZJrqou/wIDWV0DwQ5KIfYe9WXg9Xqw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@uptrace/core/node_modules/@opentelemetry/instrumentation": { + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.55.0.tgz", + "integrity": "sha512-YDCMlaQRZkziLL3t6TONRgmmGxDx6MyQDXRD0dknkkgUZtOK5+8MWft1OXzmNu6XfBOdT12MKN5rz+jHUkafKQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.55.0", + "@types/shimmer": "^1.2.0", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@uptrace/core/node_modules/@opentelemetry/resources": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.28.0.tgz", + "integrity": "sha512-cIyXSVJjGeTICENN40YSvLDAq4Y2502hGK3iN7tfdynQLKWb3XWZQEkPc+eSx47kiy11YeFAlYkEfXwR1w8kfw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.28.0", + "@opentelemetry/semantic-conventions": "1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@uptrace/core/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.28.0.tgz", + "integrity": "sha512-ceUVWuCpIao7Y5xE02Xs3nQi0tOGmMea17ecBdwtCvdo9ekmO+ijc9RFDgfifMl7XCBf41zne/1POM3LqSTZDA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.28.0", + "@opentelemetry/resources": "1.28.0", + "@opentelemetry/semantic-conventions": "1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@uptrace/core/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.27.0.tgz", + "integrity": "sha512-sAay1RrB+ONOem0OZanAR1ZI/k7yDpnOQSQmTMuGImUQb2y8EbSaCJ94FQluM74xoU03vlb2d2U90hZluL6nQg==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@uptrace/node": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/@uptrace/node/-/node-1.22.0.tgz", + "integrity": "sha512-L9lA5T89pxDkQTtgnQUUHbnrtN5thEBb42dRzC6lgZFv/p4aC2tJE/Ch1QYLDpjU1fpFmq37gd3tsuUWkKvi+g==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "~1.9.0", + "@opentelemetry/auto-instrumentations-node": "~0.52.1", + "@opentelemetry/core": "~1.28.0", + "@opentelemetry/exporter-logs-otlp-http": "~0.55.0", + "@opentelemetry/exporter-metrics-otlp-http": "~0.55.0", + "@opentelemetry/exporter-trace-otlp-http": "~0.55.0", + "@opentelemetry/id-generator-aws-xray": "~1.2.2", + "@opentelemetry/otlp-exporter-base": "~0.55.0", + "@opentelemetry/sdk-logs": "~0.55.0", + "@opentelemetry/sdk-metrics": "~1.28.0", + "@opentelemetry/sdk-node": "~0.55.0", + "@opentelemetry/sdk-trace-base": "~1.28.0", + "@uptrace/core": "1.22.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@uptrace/node/node_modules/@opentelemetry/api-logs": { + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.55.0.tgz", + "integrity": "sha512-3cpa+qI45VHYcA5c0bHM6VHo9gicv3p5mlLHNG3rLyjQU8b7e0st1rWtrUn3JbZ3DwwCfhKop4eQ9UuYlC6Pkg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@uptrace/node/node_modules/@opentelemetry/core": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.28.0.tgz", + "integrity": "sha512-ZLwRMV+fNDpVmF2WYUdBHlq0eOWtEaUJSusrzjGnBt7iSRvfjFE3RXYUZJrqou/wIDWV0DwQ5KIfYe9WXg9Xqw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@uptrace/node/node_modules/@opentelemetry/exporter-trace-otlp-http": { + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.55.0.tgz", + "integrity": "sha512-lMiNic63EVHpW+eChmLD2CieDmwQBFi72+LFbh8+5hY0ShrDGrsGP/zuT5MRh7M/vM/UZYO/2A/FYd7CMQGR7A==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.28.0", + "@opentelemetry/otlp-exporter-base": "0.55.0", + "@opentelemetry/otlp-transformer": "0.55.0", + "@opentelemetry/resources": "1.28.0", + "@opentelemetry/sdk-trace-base": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@uptrace/node/node_modules/@opentelemetry/otlp-exporter-base": { + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.55.0.tgz", + "integrity": "sha512-iHQI0Zzq3h1T6xUJTVFwmFl5Dt5y1es+fl4kM+k5T/3YvmVyeYkSiF+wHCg6oKrlUAJfk+t55kaAu3sYmt7ZYA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.28.0", + "@opentelemetry/otlp-transformer": "0.55.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@uptrace/node/node_modules/@opentelemetry/otlp-transformer": { + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.55.0.tgz", + "integrity": "sha512-kVqEfxtp6mSN2Dhpy0REo1ghP4PYhC1kMHQJ2qVlO99Pc+aigELjZDfg7/YKmL71gR6wVGIeJfiql/eXL7sQPA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.55.0", + "@opentelemetry/core": "1.28.0", + "@opentelemetry/resources": "1.28.0", + "@opentelemetry/sdk-logs": "0.55.0", + "@opentelemetry/sdk-metrics": "1.28.0", + "@opentelemetry/sdk-trace-base": "1.28.0", + "protobufjs": "^7.3.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@uptrace/node/node_modules/@opentelemetry/resources": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.28.0.tgz", + "integrity": "sha512-cIyXSVJjGeTICENN40YSvLDAq4Y2502hGK3iN7tfdynQLKWb3XWZQEkPc+eSx47kiy11YeFAlYkEfXwR1w8kfw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.28.0", + "@opentelemetry/semantic-conventions": "1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@uptrace/node/node_modules/@opentelemetry/sdk-logs": { + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.55.0.tgz", + "integrity": "sha512-TSx+Yg/d48uWW6HtjS1AD5x6WPfLhDWLl/WxC7I2fMevaiBuKCuraxTB8MDXieCNnBI24bw9ytyXrDCswFfWgA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.55.0", + "@opentelemetry/core": "1.28.0", + "@opentelemetry/resources": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.4.0 <1.10.0" + } + }, + "node_modules/@uptrace/node/node_modules/@opentelemetry/sdk-metrics": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.28.0.tgz", + "integrity": "sha512-43tqMK/0BcKTyOvm15/WQ3HLr0Vu/ucAl/D84NO7iSlv6O4eOprxSHa3sUtmYkaZWHqdDJV0AHVz/R6u4JALVQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.28.0", + "@opentelemetry/resources": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@uptrace/node/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.28.0.tgz", + "integrity": "sha512-ceUVWuCpIao7Y5xE02Xs3nQi0tOGmMea17ecBdwtCvdo9ekmO+ijc9RFDgfifMl7XCBf41zne/1POM3LqSTZDA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.28.0", + "@opentelemetry/resources": "1.28.0", + "@opentelemetry/semantic-conventions": "1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@uptrace/node/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.27.0.tgz", + "integrity": "sha512-sAay1RrB+ONOem0OZanAR1ZI/k7yDpnOQSQmTMuGImUQb2y8EbSaCJ94FQluM74xoU03vlb2d2U90hZluL6nQg==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@vitejs/plugin-vue": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.1.tgz", + "integrity": "sha512-cxh314tzaWwOLqVes2gnnCtvBDcM1UMdn+iFR+UjAn411dPT3tOmqrJjbMd7koZpMAmBM/GqeV4n9ge7JSiJJQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@vitest/coverage-v8": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.0.5.tgz", + "integrity": "sha512-zOOWIsj5fHh3jjGwQg+P+J1FW3s4jBu1Zqga0qW60yutsBtqEqNEJKWYh7cYn1yGD+1bdPsPdC/eL4eVK56xMg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.3.0", + "@bcoe/v8-coverage": "^1.0.2", + "debug": "^4.4.0", + "istanbul-lib-coverage": "^3.2.2", + "istanbul-lib-report": "^3.0.1", + "istanbul-lib-source-maps": "^5.0.6", + "istanbul-reports": "^3.1.7", + "magic-string": "^0.30.17", + "magicast": "^0.3.5", + "std-env": "^3.8.0", + "test-exclude": "^7.0.1", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@vitest/browser": "3.0.5", + "vitest": "3.0.5" + }, + "peerDependenciesMeta": { + "@vitest/browser": { + "optional": true + } + } + }, + "node_modules/@vitest/expect": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.0.5.tgz", + "integrity": "sha512-nNIOqupgZ4v5jWuQx2DSlHLEs7Q4Oh/7AYwNyE+k0UQzG7tSmjPXShUikn1mpNGzYEN2jJbTvLejwShMitovBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "3.0.5", + "@vitest/utils": "3.0.5", + "chai": "^5.1.2", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.0.5.tgz", + "integrity": "sha512-CLPNBFBIE7x6aEGbIjaQAX03ZZlBMaWwAjBdMkIf/cAn6xzLTiM3zYqO/WAbieEjsAZir6tO71mzeHZoodThvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "3.0.5", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.17" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0 || ^6.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.0.5.tgz", + "integrity": "sha512-CjUtdmpOcm4RVtB+up8r2vVDLR16Mgm/bYdkGFe3Yj/scRfCpbSi2W/BDSDcFK7ohw8UXvjMbOp9H4fByd/cOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.0.5.tgz", + "integrity": "sha512-BAiZFityFexZQi2yN4OX3OkJC6scwRo8EhRB0Z5HIGGgd2q+Nq29LgHU/+ovCtd0fOfXj5ZI6pwdlUmC5bpi8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "3.0.5", + "pathe": "^2.0.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.0.5.tgz", + "integrity": "sha512-GJPZYcd7v8QNUJ7vRvLDmRwl+a1fGg4T/54lZXe+UOGy47F9yUfE18hRCtXL5aHN/AONu29NGzIXSVFh9K0feA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.0.5", + "magic-string": "^0.30.17", + "pathe": "^2.0.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.0.5.tgz", + "integrity": "sha512-5fOzHj0WbUNqPK6blI/8VzZdkBlQLnT25knX0r4dbZI9qoZDf3qAdjoMmDcLG5A83W6oUUFJgUd0EYBc2P5xqg==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyspy": "^3.0.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/ui": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-3.0.5.tgz", + "integrity": "sha512-gw2noso6WI+2PeMVCZFntdATS6xl9qhQcbhkPQ9sOmx/Xn0f4Bx4KDSbD90jpJPF0l5wOzSoGCmKyVR3W612mg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "3.0.5", + "fflate": "^0.8.2", + "flatted": "^3.3.2", + "pathe": "^2.0.2", + "sirv": "^3.0.0", + "tinyglobby": "^0.2.10", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "vitest": "3.0.5" + } + }, + "node_modules/@vitest/utils": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.0.5.tgz", + "integrity": "sha512-N9AX0NUoUtVwKwy21JtwzaqR5L5R5A99GAbrHfCCXK1lp593i/3AZAXhSP43wRQuxYsflrdzEfXZFo1reR1Nkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.0.5", + "loupe": "^3.1.2", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.13.tgz", + "integrity": "sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.25.3", + "@vue/shared": "3.5.13", + "entities": "^4.5.0", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.0" + } + }, + "node_modules/@vue/compiler-core/node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.13.tgz", + "integrity": "sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.13", + "@vue/shared": "3.5.13" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.13.tgz", + "integrity": "sha512-6VdaljMpD82w6c2749Zhf5T9u5uLBWKnVue6XWxprDobftnletJ8+oel7sexFfM3qIxNmVE7LSFGTpv6obNyaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.25.3", + "@vue/compiler-core": "3.5.13", + "@vue/compiler-dom": "3.5.13", + "@vue/compiler-ssr": "3.5.13", + "@vue/shared": "3.5.13", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.11", + "postcss": "^8.4.48", + "source-map-js": "^1.2.0" + } + }, + "node_modules/@vue/compiler-sfc/node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.13.tgz", + "integrity": "sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.13", + "@vue/shared": "3.5.13" + } + }, + "node_modules/@vue/devtools-api": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.7.1.tgz", + "integrity": "sha512-Cexc8GimowoDkJ6eNelOPdYIzsu2mgNyp0scOQ3tiaYSb9iok6LOESSsJvHaI+ib3joRfqRJNLkHFjhNuWA5dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/devtools-kit": "^7.7.1" + } + }, + "node_modules/@vue/devtools-kit": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.7.1.tgz", + "integrity": "sha512-yhZ4NPnK/tmxGtLNQxmll90jIIXdb2jAhPF76anvn5M/UkZCiLJy28bYgPIACKZ7FCosyKoaope89/RsFJll1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/devtools-shared": "^7.7.1", + "birpc": "^0.2.19", + "hookable": "^5.5.3", + "mitt": "^3.0.1", + "perfect-debounce": "^1.0.0", + "speakingurl": "^14.0.1", + "superjson": "^2.2.1" + } + }, + "node_modules/@vue/devtools-shared": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.7.1.tgz", + "integrity": "sha512-BtgF7kHq4BHG23Lezc/3W2UhK2ga7a8ohAIAGJMBr4BkxUFzhqntQtCiuL1ijo2ztWnmusymkirgqUrXoQKumA==", + "dev": true, + "license": "MIT", + "dependencies": { + "rfdc": "^1.4.1" + } + }, + "node_modules/@vue/reactivity": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.13.tgz", + "integrity": "sha512-NaCwtw8o48B9I6L1zl2p41OHo/2Z4wqYGGIK1Khu5T7yxrn+ATOixn/Udn2m+6kZKB/J7cuT9DbWWhRxqixACg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.13" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.13.tgz", + "integrity": "sha512-Fj4YRQ3Az0WTZw1sFe+QDb0aXCerigEpw418pw1HBUKFtnQHWzwojaukAs2X/c9DQz4MQ4bsXTGlcpGxU/RCIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.13", + "@vue/shared": "3.5.13" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.13.tgz", + "integrity": "sha512-dLaj94s93NYLqjLiyFzVs9X6dWhTdAlEAciC3Moq7gzAc13VJUdCnjjRurNM6uTLFATRHexHCTu/Xp3eW6yoog==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.13", + "@vue/runtime-core": "3.5.13", + "@vue/shared": "3.5.13", + "csstype": "^3.1.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.13.tgz", + "integrity": "sha512-wAi4IRJV/2SAW3htkTlB+dHeRmpTiVIK1OGLWV1yeStVSebSQQOwGwIq0D3ZIoBj2C2qpgz5+vX9iEBkTdk5YA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.13", + "@vue/shared": "3.5.13" + }, + "peerDependencies": { + "vue": "3.5.13" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.13.tgz", + "integrity": "sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vueuse/core": { + "version": "12.5.0", + "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-12.5.0.tgz", + "integrity": "sha512-GVyH1iYqNANwcahAx8JBm6awaNgvR/SwZ1fjr10b8l1HIgDp82ngNbfzJUgOgWEoxjL+URAggnlilAEXwCOZtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/web-bluetooth": "^0.0.20", + "@vueuse/metadata": "12.5.0", + "@vueuse/shared": "12.5.0", + "vue": "^3.5.13" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/metadata": { + "version": "12.5.0", + "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-12.5.0.tgz", + "integrity": "sha512-Ui7Lo2a7AxrMAXRF+fAp9QsXuwTeeZ8fIB9wsLHqzq9MQk+2gMYE2IGJW48VMJ8ecvCB3z3GsGLKLbSasQ5Qlg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/shared": { + "version": "12.5.0", + "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-12.5.0.tgz", + "integrity": "sha512-vMpcL1lStUU6O+kdj6YdHDixh0odjPAUM15uJ9f7MY781jcYkIwFA4iv2EfoIPO6vBmvutI1HxxAwmf0cx5ISQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "vue": "^3.5.13" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", + "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/helper-numbers": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", + "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", + "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", + "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", + "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.13.2", + "@webassemblyjs/helper-api-error": "1.13.2", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", + "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", + "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/wasm-gen": "1.14.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", + "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", + "license": "MIT", + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", + "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", + "license": "Apache-2.0", + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", + "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", + "license": "MIT" + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", + "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/helper-wasm-section": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-opt": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1", + "@webassemblyjs/wast-printer": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", + "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", + "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", + "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-api-error": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", + "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "license": "BSD-3-Clause" + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "license": "Apache-2.0" + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/abstract-logging": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz", + "integrity": "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==", + "license": "MIT" + }, + "node_modules/acorn": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-attributes": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", + "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", + "license": "MIT", + "peerDependencies": { + "acorn": "^8" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/aggregate-error": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-4.0.1.tgz", + "integrity": "sha512-0poP0T7el6Vq3rstR8Mn4V/IQrpBLO6POkUSrN7RhyY+GF/InCFShQzsQ39T25gkHhLgSLByyAz+Kjb+c2L98w==", + "license": "MIT", + "dependencies": { + "clean-stack": "^4.0.0", + "indent-string": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/algoliasearch": { + "version": "5.20.1", + "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.20.1.tgz", + "integrity": "sha512-SiCOCVBCQUg/aWkfMnjT+8TQxNNFlPZTI7v8y4+aZXzJg6zDIzKy9KcYVS4sc+xk5cwW5hyJ+9z836f4+wvgzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-abtesting": "5.20.1", + "@algolia/client-analytics": "5.20.1", + "@algolia/client-common": "5.20.1", + "@algolia/client-insights": "5.20.1", + "@algolia/client-personalization": "5.20.1", + "@algolia/client-query-suggestions": "5.20.1", + "@algolia/client-search": "5.20.1", + "@algolia/ingestion": "1.20.1", + "@algolia/monitoring": "1.20.1", + "@algolia/recommend": "5.20.1", + "@algolia/requester-browser-xhr": "5.20.1", + "@algolia/requester-fetch": "5.20.1", + "@algolia/requester-node-http": "5.20.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/amqplib": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/amqplib/-/amqplib-0.10.5.tgz", + "integrity": "sha512-Dx5zmy0Ur+Q7LPPdhz+jx5IzmJBoHd15tOeAfQ8SuvEtyPJ20hBemhOBA4b1WeORCRa0ENM/kHCzmem1w/zHvQ==", + "license": "MIT", + "dependencies": { + "@acuminous/bitsyntax": "^0.1.2", + "buffer-more-ints": "~1.0.0", + "url-parse": "~1.5.10" + }, + "engines": { + "node": ">=10" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "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, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/archiver": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-7.0.1.tgz", + "integrity": "sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "archiver-utils": "^5.0.2", + "async": "^3.2.4", + "buffer-crc32": "^1.0.0", + "readable-stream": "^4.0.0", + "readdir-glob": "^1.1.2", + "tar-stream": "^3.0.0", + "zip-stream": "^6.0.1" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/archiver-utils": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-5.0.2.tgz", + "integrity": "sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "glob": "^10.0.0", + "graceful-fs": "^4.2.0", + "is-stream": "^2.0.1", + "lazystream": "^1.0.0", + "lodash": "^4.17.15", + "normalize-path": "^3.0.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/archiver-utils/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/archiver-utils/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/archiver-utils/node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/archiver-utils/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/archiver-utils/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/archiver-utils/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true, + "license": "MIT" + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/array-each": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", + "integrity": "sha512-zHjL5SZa68hkKHBFBK6DJCTtr9sfTCPCaph/L7tMSLcTFgy+zX7E+6q5UArbtOtMBCtxdICpfTCspRse+ywyXA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-slice": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz", + "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true, + "license": "MIT" + }, + "node_modules/async-lock": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/async-lock/-/async-lock-1.4.1.tgz", + "integrity": "sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/atomic-sleep": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", + "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/avvio": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/avvio/-/avvio-9.1.0.tgz", + "integrity": "sha512-fYASnYi600CsH/j9EQov7lECAniYiBFiiAtBNuZYLA2leLe9qOvZzqYHFjtIj6gD2VMoMLP14834LFWvr4IfDw==", + "license": "MIT", + "dependencies": { + "@fastify/error": "^4.0.0", + "fastq": "^1.17.1" + } + }, + "node_modules/b4a": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.7.tgz", + "integrity": "sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==", + "dev": true, + "license": "Apache-2.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==", + "license": "MIT" + }, + "node_modules/bare-events": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.5.4.tgz", + "integrity": "sha512-+gFfDkR8pj4/TrWCGUGWmJIkBwuxPS5F+a5yWjOHQt2hHvNZd5YLzadjmDUtFmMM4y429bnKLa8bYBMHcYdnQA==", + "dev": true, + "license": "Apache-2.0", + "optional": true + }, + "node_modules/bare-fs": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.0.1.tgz", + "integrity": "sha512-ilQs4fm/l9eMfWY2dY0WCIUplSUp7U0CT1vrqMg1MUdeZl4fypu5UP0XcDBK5WBQPJAKP1b7XEodISmekH/CEg==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "bare-events": "^2.0.0", + "bare-path": "^3.0.0", + "bare-stream": "^2.0.0" + }, + "engines": { + "bare": ">=1.7.0" + } + }, + "node_modules/bare-os": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.4.0.tgz", + "integrity": "sha512-9Ous7UlnKbe3fMi7Y+qh0DwAup6A1JkYgPnjvMDNOlmnxNRQvQ/7Nst+OnUQKzk0iAT0m9BisbDVp9gCv8+ETA==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "engines": { + "bare": ">=1.6.0" + } + }, + "node_modules/bare-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.0.0.tgz", + "integrity": "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "bare-os": "^3.0.1" + } + }, + "node_modules/bare-stream": { + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.6.5.tgz", + "integrity": "sha512-jSmxKJNJmHySi6hC42zlZnq00rga4jjxcgNZjY9N5WlOe/iOoGRtdwGsHzQv2RlH2KOYMwGUXhf2zXd32BA9RA==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "streamx": "^2.21.0" + }, + "peerDependencies": { + "bare-buffer": "*", + "bare-events": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + }, + "bare-events": { + "optional": true + } + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, + "node_modules/bignumber.js": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", + "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/birpc": { + "version": "0.2.19", + "resolved": "https://registry.npmjs.org/birpc/-/birpc-0.2.19.tgz", + "integrity": "sha512-5WeXXAvTmitV1RqJFppT5QtUiz2p1mRSYU000Jkft5ZUCLJIk4uQriYNO50HknxKwM6jd8utNc66K1qGIwwWBQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bl/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/bl/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bowser": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", + "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==", + "license": "MIT" + }, + "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==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", + "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.1" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/buffer-crc32": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz", + "integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, + "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==", + "license": "MIT" + }, + "node_modules/buffer-more-ints": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-more-ints/-/buffer-more-ints-1.0.0.tgz", + "integrity": "sha512-EMetuGFz5SLsT0QTnXzINh4Ksr+oo4i+UGTXEshiGCQWnsgSs7ZhJ8fzlwQ+OzEMs0MpDAMr1hxnblp5a4vcHg==", + "license": "MIT" + }, + "node_modules/buildcheck": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/buildcheck/-/buildcheck-0.0.6.tgz", + "integrity": "sha512-8f9ZJCUXyT1M35Jx7MkBgmBMo3oHTTBIPLiY9xyL0pl3T5RwcPEY8cUHr5LBNfu/fk6c2T4DJZuVM/8ZZT2D2A==", + "dev": true, + "optional": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/byline": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/byline/-/byline-5.0.0.tgz", + "integrity": "sha512-s6webAy+R4SR8XVuJWt2V2rGvhnrhxN+9S15GNuTK3wKPOXFF6RNc+8ug2XhH+2s4f+uudG4kUVYmYOQWL2g0Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", + "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", + "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "license": "MIT", + "dependencies": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, + "node_modules/camelcase": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-8.0.0.tgz", + "integrity": "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==", + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001699", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001699.tgz", + "integrity": "sha512-b+uH5BakXZ9Do9iK+CkDmctUSEqZl+SP056vc5usa0PL+ev5OHw003rZXcnjNDv3L8P5j6rwT6C0BPKSikW08w==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/capital-case": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/capital-case/-/capital-case-1.0.4.tgz", + "integrity": "sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==", + "license": "MIT", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3", + "upper-case-first": "^2.0.2" + } + }, + "node_modules/cargo-cp-artifact": { + "version": "0.1.9", + "resolved": "https://registry.npmjs.org/cargo-cp-artifact/-/cargo-cp-artifact-0.1.9.tgz", + "integrity": "sha512-6F+UYzTaGB+awsTXg0uSJA1/b/B3DDJzpKVRu0UmyI7DmNeaAl2RFHuTGIN6fEgpadRxoXGb7gbC1xo4C3IdyA==", + "license": "MIT", + "bin": { + "cargo-cp-artifact": "bin/cargo-cp-artifact.js" + } + }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/chai": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.2.tgz", + "integrity": "sha512-aGtmf24DW6MLHHG5gCx4zaI3uBq3KRtxeVs0DjFH6Z0rDNbsvTxFASFvdj79pxjxZ8/5u3PIiN3IwEIQkiiuPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/chalk": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/change-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/change-case/-/change-case-4.1.2.tgz", + "integrity": "sha512-bSxY2ws9OtviILG1EiY5K7NNxkqg/JnRnFxLtKQ96JaviiIxi7djMrSd0ECT9AC+lttClmYwKw53BWpOMblo7A==", + "license": "MIT", + "dependencies": { + "camel-case": "^4.1.2", + "capital-case": "^1.0.4", + "constant-case": "^3.0.4", + "dot-case": "^3.0.4", + "header-case": "^2.0.4", + "no-case": "^3.0.4", + "param-case": "^3.0.4", + "pascal-case": "^3.1.2", + "path-case": "^3.0.4", + "sentence-case": "^3.0.4", + "snake-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "license": "MIT" + }, + "node_modules/check-error": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", + "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true, + "license": "ISC" + }, + "node_modules/chrome-trace-event": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", + "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", + "license": "MIT", + "engines": { + "node": ">=6.0" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", + "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", + "license": "MIT" + }, + "node_modules/clean-stack": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-4.2.0.tgz", + "integrity": "sha512-LYv6XPxoyODi36Dp976riBtSY27VmFo+MKqEU9QCCWyTrdEPDog+RWA7xQWHi6Vbp61j5c4cdzzX1NidnwtUWg==", + "license": "MIT", + "dependencies": { + "escape-string-regexp": "5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "license": "MIT", + "dependencies": { + "restore-cursor": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "license": "ISC", + "engines": { + "node": ">= 12" + } + }, + "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==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/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==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/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==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "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==", + "license": "MIT" + }, + "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==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/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==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/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==", + "license": "MIT", + "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/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/cloudevents": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/cloudevents/-/cloudevents-8.0.2.tgz", + "integrity": "sha512-93KKRR61D2NNE+2lg2HmLbl17beVTKpf1UYd/8BcXpuiDxbU2fb8gAfriSmVGmj1xX/Oh2t5Fh/xGOWFdu6F4A==", + "license": "Apache-2.0", + "dependencies": { + "ajv": "^8.11.0", + "ajv-formats": "^2.1.1", + "json-bigint": "^1.0.0", + "process": "^0.11.10", + "util": "^0.12.4", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">=16 <=22" + } + }, + "node_modules/cloudevents/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/cluster-key-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", + "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/code-block-writer": { + "version": "13.0.3", + "resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-13.0.3.tgz", + "integrity": "sha512-Oofo0pq3IKnsFtuHqSF7TqBfr71aeyZDVJ0HpmqB7FBM2qEigL0iPONSCZSO9pE9dZTAxANe5XHG9Uy0YMv8cg==", + "license": "MIT" + }, + "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==", + "license": "MIT", + "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==", + "license": "MIT" + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/commist": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/commist/-/commist-3.2.0.tgz", + "integrity": "sha512-4PIMoPniho+LqXmpS5d3NuGYncG6XWlkBSVGiWycL22dd42OYdUGil2CWuzklaJoNxyxUSpO4MKIBU94viWNAw==", + "license": "MIT" + }, + "node_modules/compress-commons": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-6.0.2.tgz", + "integrity": "sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "crc-32": "^1.2.0", + "crc32-stream": "^6.0.0", + "is-stream": "^2.0.1", + "normalize-path": "^3.0.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/compress-commons/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "license": "MIT" + }, + "node_modules/concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "engines": [ + "node >= 6.0" + ], + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/concat-stream/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/constant-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/constant-case/-/constant-case-3.0.4.tgz", + "integrity": "sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ==", + "license": "MIT", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3", + "upper-case": "^2.0.2" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", + "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/copy-anything": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-3.0.5.tgz", + "integrity": "sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-what": "^4.1.8" + }, + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, + "node_modules/cpu-features": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.10.tgz", + "integrity": "sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "dependencies": { + "buildcheck": "~0.0.6", + "nan": "^2.19.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/crc32-stream": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-6.0.0.tgz", + "integrity": "sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==", + "dev": true, + "license": "MIT", + "dependencies": { + "crc-32": "^1.2.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "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, + "license": "MIT" + }, + "node_modules/cross-fetch": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", + "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", + "license": "MIT", + "dependencies": { + "node-fetch": "^2.6.12" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "dev": true, + "license": "MIT" + }, + "node_modules/date-fns": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz", + "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, + "node_modules/dateformat": { + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz", + "integrity": "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-eql": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "license": "MIT", + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/del": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/del/-/del-7.1.0.tgz", + "integrity": "sha512-v2KyNk7efxhlyHpjEvfyxaAihKKK0nWCuf6ZtqZcFFpQRG0bJ12Qsr0RpvsICMjAAZ8DOVCxrlqpxISlMHC4Kg==", + "license": "MIT", + "dependencies": { + "globby": "^13.1.2", + "graceful-fs": "^4.2.10", + "is-glob": "^4.0.3", + "is-path-cwd": "^3.0.0", + "is-path-inside": "^4.0.0", + "p-map": "^5.5.0", + "rimraf": "^3.0.2", + "slash": "^4.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/del/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==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/del/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", + "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/del/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/del/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/detect-file": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", + "integrity": "sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/diff": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", + "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/docker-compose": { + "version": "0.24.8", + "resolved": "https://registry.npmjs.org/docker-compose/-/docker-compose-0.24.8.tgz", + "integrity": "sha512-plizRs/Vf15H+GCVxq2EUvyPK7ei9b/cVesHvjnX4xaXjM9spHe2Ytq0BitndFgvTJ3E3NljPNUEl7BAN43iZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "yaml": "^2.2.2" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/docker-modem": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-3.0.8.tgz", + "integrity": "sha512-f0ReSURdM3pcKPNS30mxOHSbaFLcknGmQjwSfmbcdOw1XWKXVhukM3NJHhr7NpY9BIyyWQb0EBo3KQvvuU5egQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "debug": "^4.1.1", + "readable-stream": "^3.5.0", + "split-ca": "^1.0.1", + "ssh2": "^1.11.0" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/docker-modem/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/dockerode": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/dockerode/-/dockerode-3.3.5.tgz", + "integrity": "sha512-/0YNa3ZDNeLr/tSckmD69+Gq+qVNhvKfAHNeZJBnp7EOP6RGKV8ORrJHkUn20So5wU+xxT7+1n5u8PjHbfjbSA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@balena/dockerignore": "^1.0.2", + "docker-modem": "^3.0.0", + "tar-fs": "~2.0.1" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/dockerode/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/dockerode/node_modules/tar-fs": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.0.1.tgz", + "integrity": "sha512-6tzWDMeroL87uF/+lin46k+Q+46rAJ0SyPGz7OW7wTgblI273hsBqk2C1j0/xNadNLKDTUL9BukSjB7cwgmlPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.0.0" + } + }, + "node_modules/dockerode/node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "license": "MIT", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/dotenv": { + "version": "16.4.7", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", + "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/duplexify": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz", + "integrity": "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.4.1", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1", + "stream-shift": "^1.0.2" + } + }, + "node_modules/duplexify/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.96", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.96.tgz", + "integrity": "sha512-8AJUW6dh75Fm/ny8+kZKJzI1pgoE8bKLZlzDU2W1ENd+DXKJrx7I7l9hb8UWR4ojlnb5OlixMt00QWiYJoVw1w==", + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" + }, + "node_modules/emoji-regex-xs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex-xs/-/emoji-regex-xs-1.0.0.tgz", + "integrity": "sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==", + "dev": true, + "license": "MIT" + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.18.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", + "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz", + "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==", + "license": "MIT" + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", + "integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.23.1", + "@esbuild/android-arm": "0.23.1", + "@esbuild/android-arm64": "0.23.1", + "@esbuild/android-x64": "0.23.1", + "@esbuild/darwin-arm64": "0.23.1", + "@esbuild/darwin-x64": "0.23.1", + "@esbuild/freebsd-arm64": "0.23.1", + "@esbuild/freebsd-x64": "0.23.1", + "@esbuild/linux-arm": "0.23.1", + "@esbuild/linux-arm64": "0.23.1", + "@esbuild/linux-ia32": "0.23.1", + "@esbuild/linux-loong64": "0.23.1", + "@esbuild/linux-mips64el": "0.23.1", + "@esbuild/linux-ppc64": "0.23.1", + "@esbuild/linux-riscv64": "0.23.1", + "@esbuild/linux-s390x": "0.23.1", + "@esbuild/linux-x64": "0.23.1", + "@esbuild/netbsd-x64": "0.23.1", + "@esbuild/openbsd-arm64": "0.23.1", + "@esbuild/openbsd-x64": "0.23.1", + "@esbuild/sunos-x64": "0.23.1", + "@esbuild/win32-arm64": "0.23.1", + "@esbuild/win32-ia32": "0.23.1", + "@esbuild/win32-x64": "0.23.1" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "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, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==", + "license": "MIT", + "dependencies": { + "homedir-polyfill": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expect-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.1.0.tgz", + "integrity": "sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, + "node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "license": "MIT", + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/external-editor/node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "license": "MIT", + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/fast-copy": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/fast-copy/-/fast-copy-3.0.2.tgz", + "integrity": "sha512-dl0O9Vhju8IrcLndv2eU4ldt1ftXMqqfgN4H1cpmGV7P6jeB9FwpN9a2c8DPGE1Ys88rNUJVYDHq73CGAGOPfQ==", + "license": "MIT" + }, + "node_modules/fast-decode-uri-component": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz", + "integrity": "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==", + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "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==", + "license": "MIT" + }, + "node_modules/fast-json-stringify": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-6.0.1.tgz", + "integrity": "sha512-s7SJE83QKBZwg54dIbD5rCtzOBVD43V1ReWXXYqBgwCwHLYAAT0RQc/FmrQglXqWPpz6omtryJQOau5jI4Nrvg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "@fastify/merge-json-schemas": "^0.2.0", + "ajv": "^8.12.0", + "ajv-formats": "^3.0.1", + "fast-uri": "^3.0.0", + "json-schema-ref-resolver": "^2.0.0", + "rfdc": "^1.2.0" + } + }, + "node_modules/fast-json-stringify/node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/fast-querystring": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fast-querystring/-/fast-querystring-1.1.2.tgz", + "integrity": "sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==", + "license": "MIT", + "dependencies": { + "fast-decode-uri-component": "^1.0.1" + } + }, + "node_modules/fast-redact": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.5.0.tgz", + "integrity": "sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", + "license": "MIT" + }, + "node_modules/fast-unique-numbers": { + "version": "8.0.13", + "resolved": "https://registry.npmjs.org/fast-unique-numbers/-/fast-unique-numbers-8.0.13.tgz", + "integrity": "sha512-7OnTFAVPefgw2eBJ1xj2PGGR9FwYzSUso9decayHgCDX4sJkHLdcsYTytTg+tYv+wKF3U8gJuSBz2jJpQV4u/g==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.23.8", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.1.0" + } + }, + "node_modules/fast-uri": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", + "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fast-xml-parser": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.1.tgz", + "integrity": "sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + }, + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + } + ], + "license": "MIT", + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/fastify": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/fastify/-/fastify-5.2.1.tgz", + "integrity": "sha512-rslrNBF67eg8/Gyn7P2URV8/6pz8kSAscFL4EThZJ8JBMaXacVdVE4hmUcnPNKERl5o/xTiBSLfdowBRhVF1WA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "@fastify/ajv-compiler": "^4.0.0", + "@fastify/error": "^4.0.0", + "@fastify/fast-json-stringify-compiler": "^5.0.0", + "@fastify/proxy-addr": "^5.0.0", + "abstract-logging": "^2.0.1", + "avvio": "^9.0.0", + "fast-json-stringify": "^6.0.0", + "find-my-way": "^9.0.0", + "light-my-request": "^6.0.0", + "pino": "^9.0.0", + "process-warning": "^4.0.0", + "rfdc": "^1.3.1", + "secure-json-parse": "^3.0.1", + "semver": "^7.6.0", + "toad-cache": "^3.7.0" + } + }, + "node_modules/fastify-plugin": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/fastify-plugin/-/fastify-plugin-5.0.1.tgz", + "integrity": "sha512-HCxs+YnRaWzCl+cWRYFnHmeRFyR5GVnJTAaCJQiYzQSDwK9MgJdyAsuL3nh0EWRCYMgQ5MeziymvmAhUHYHDUQ==", + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.0.tgz", + "integrity": "sha512-7SFSRCNjBQIZH/xZR3iy5iQYR8aGBE0h3VG6/cwlbrpdciNYBMotQav8c1XI3HjHH+NikUpP53nPdlZSdWmFzA==", + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fdir": { + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.3.tgz", + "integrity": "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/feed": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/feed/-/feed-4.2.2.tgz", + "integrity": "sha512-u5/sxGfiMfZNtJ3OvQpXcvotFpYkL0n9u9mM2vkui2nGo8b4wvDkJ8gAkYqbA8QpGyFCv3RK0Z+Iv+9veCS9bQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-js": "^1.6.11" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "dev": true, + "license": "MIT" + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-my-way": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-9.2.0.tgz", + "integrity": "sha512-d3uCir8Hmg7W1Ywp8nKf2lJJYU9Nwinvo+1D39Dn09nz65UKXIxUh7j7K8zeWhxqe1WrkS7FJyON/Q/3lPoc6w==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-querystring": "^1.0.0", + "safe-regex2": "^4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/findup-sync": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-5.0.0.tgz", + "integrity": "sha512-MzwXju70AuyflbgeOhzvQWAvvQdo1XL0A9bVvlXsYcFEBM87WR4OakL4OfZq+QRmr+duJubio+UtNQCPsVESzQ==", + "license": "MIT", + "dependencies": { + "detect-file": "^1.0.0", + "is-glob": "^4.0.3", + "micromatch": "^4.0.4", + "resolve-dir": "^1.0.1" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/fined": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fined/-/fined-2.0.0.tgz", + "integrity": "sha512-OFRzsL6ZMHz5s0JrsEr+TpdGNCtrVtnuG3x1yzGNiQHT0yaDnXAj8V/lWcpJVrnoDpcwXcASxAZYbuXda2Y82A==", + "license": "MIT", + "dependencies": { + "expand-tilde": "^2.0.2", + "is-plain-object": "^5.0.0", + "object.defaults": "^1.1.0", + "object.pick": "^1.3.0", + "parse-filepath": "^1.0.2" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/flagged-respawn": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-2.0.0.tgz", + "integrity": "sha512-Gq/a6YCi8zexmGHMuJwahTGzXlAZAOsbCVKduWXC6TlLCjjFRlExMJc4GC2NYPYZ0r/brw9P7CpRgQmlPVeOoA==", + "license": "MIT", + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/flatted": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", + "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", + "dev": true, + "license": "ISC" + }, + "node_modules/focus-trap": { + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.6.4.tgz", + "integrity": "sha512-xx560wGBk7seZ6y933idtjJQc1l+ck+pI3sKvhKozdBV1dRZoKhkW5xoCaFv9tQiX5RH1xfSxjuNu6g+lmN/gw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tabbable": "^6.2.0" + } + }, + "node_modules/for-each": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.4.tgz", + "integrity": "sha512-kKaIINnFpzW6ffJNDjjyjrk21BkDx38c0xa/klsT8VzLCaMEefv4ZTacrcVR4DmgTeBra++jMDAfS/tS799YDw==", + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/for-own": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", + "integrity": "sha512-0OABksIGrxKK8K4kynWkQ7y1zounQxP+CWnyclVwj81KW3vlLlGUx57DKGcP/LH216GzqnstnPocF16Nxs0Ycg==", + "license": "MIT", + "dependencies": { + "for-in": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/form-data": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.2.tgz", + "integrity": "sha512-GgwY0PS7DbXqajuGf4OYlsrIu3zgxD6Vvql43IBhm6MahqA5SK/7mwhtNj2AdH2z35YR34ujJ7BN+3fFC3jP5Q==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/forwarded-parse": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/forwarded-parse/-/forwarded-parse-2.1.2.tgz", + "integrity": "sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw==", + "license": "MIT" + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true, + "license": "MIT" + }, + "node_modules/fs-monkey": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.6.tgz", + "integrity": "sha512-b1FMfwetIKymC0eioW7mTywihSQE4oLzQn1dB6rZB5fx/3NpNEdAWeCSMB+60/AeT0TCXsxzAlcYVEFCTAksWg==", + "license": "Unlicense" + }, + "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==", + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gaxios": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz", + "integrity": "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==", + "license": "Apache-2.0", + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "is-stream": "^2.0.0", + "node-fetch": "^2.6.9", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/gaxios/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==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gcp-metadata": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.1.tgz", + "integrity": "sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A==", + "license": "Apache-2.0", + "dependencies": { + "gaxios": "^6.1.1", + "google-logging-utils": "^0.0.2", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/generic-pool": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz", + "integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "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==", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz", + "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", + "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "function-bind": "^1.1.2", + "get-proto": "^1.0.0", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-port": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", + "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-tsconfig": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.0.tgz", + "integrity": "sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/git-cliff": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/git-cliff/-/git-cliff-2.8.0.tgz", + "integrity": "sha512-iKF5QTXAb9+iVvmu5HpnMPWYw7fs74xkpAaRbSf29+dZaMTTNRIUST/y+Ir2S1bDUWWJNjXlwT9ZT62JuYLQnA==", + "dev": true, + "license": "MIT OR Apache-2.0", + "dependencies": { + "execa": "^8.0.1" + }, + "bin": { + "git-cliff": "lib/cli/cli.js" + }, + "engines": { + "node": ">=18.19 || >=20.6 || >=21" + }, + "optionalDependencies": { + "git-cliff-darwin-arm64": "2.8.0", + "git-cliff-darwin-x64": "2.8.0", + "git-cliff-linux-arm64": "2.8.0", + "git-cliff-linux-x64": "2.8.0", + "git-cliff-windows-arm64": "2.8.0", + "git-cliff-windows-x64": "2.8.0" + } + }, + "node_modules/git-cliff-darwin-arm64": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/git-cliff-darwin-arm64/-/git-cliff-darwin-arm64-2.8.0.tgz", + "integrity": "sha512-rurUV2d1Z2n+c2+wUrO0gZaFb3c1G+ej0bPfKTPfde/CblxiysMkh+4dz23NrVbc8IlS5rSYv/JFGVaVSBNJRw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/git-cliff-darwin-x64": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/git-cliff-darwin-x64/-/git-cliff-darwin-x64-2.8.0.tgz", + "integrity": "sha512-Wtj+FGWZBWmeYUAGlkfz7QPz4+VVxxDPMhQ/7iwKVA3iryIX0slGfzYpqMurEFnTAMr0r+4IU3Q4O/ib7iUscg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/git-cliff-linux-arm64": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/git-cliff-linux-arm64/-/git-cliff-linux-arm64-2.8.0.tgz", + "integrity": "sha512-k4RdfMdORXyefznWlQb+7wDgo7XgQF9qg8hJC34bwyJK2sODirrGau3uTx1/9Fi37g+pAOM7wM+LYppHCTZ2bQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/git-cliff-linux-x64": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/git-cliff-linux-x64/-/git-cliff-linux-x64-2.8.0.tgz", + "integrity": "sha512-FcWX4GHgodYrQlZR03fzooanStgR03JNWvyaMQB1asplQ18nlziK2UyA+PESCIxOQmeLXauqoCApfzmdtp5myg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/git-cliff-windows-arm64": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/git-cliff-windows-arm64/-/git-cliff-windows-arm64-2.8.0.tgz", + "integrity": "sha512-GJSrqmBVTbMtBJI3/YCDxLviZZDgYgnKqYgquBk2u2AELAnnuWFnVFQ7ZEBUqgFF2UJu9EdV2Nv6MV8d/wnP0g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/git-cliff-windows-x64": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/git-cliff-windows-x64/-/git-cliff-windows-x64-2.8.0.tgz", + "integrity": "sha512-8jl0YMXPYjUmVygUEeQ4wf1zte3Rv8LPq1sIklUKl80XE4g2Gm/8EIWbKpUPLQH6IncRwepY6VuMgpVpPXbwNw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/glob": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.1.tgz", + "integrity": "sha512-zrQDm8XPnYEKawJScsnM0QzobJxlT/kHOOlRTio8IH/GrmxRE5fjllkzdaHclIuNjUQTJYH2xHNIGfdpJkDJUw==", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^4.0.1", + "minimatch": "^10.0.0", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "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==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "license": "BSD-2-Clause" + }, + "node_modules/global-modules": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", + "license": "MIT", + "dependencies": { + "global-prefix": "^1.0.1", + "is-windows": "^1.0.1", + "resolve-dir": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/global-prefix": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", + "integrity": "sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==", + "license": "MIT", + "dependencies": { + "expand-tilde": "^2.0.2", + "homedir-polyfill": "^1.0.1", + "ini": "^1.3.4", + "is-windows": "^1.0.1", + "which": "^1.2.14" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/global-prefix/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/globby": { + "version": "13.2.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.2.2.tgz", + "integrity": "sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==", + "license": "MIT", + "dependencies": { + "dir-glob": "^3.0.1", + "fast-glob": "^3.3.0", + "ignore": "^5.2.4", + "merge2": "^1.4.1", + "slash": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/google-auth-library": { + "version": "9.15.1", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.15.1.tgz", + "integrity": "sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==", + "license": "Apache-2.0", + "dependencies": { + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "gaxios": "^6.1.1", + "gcp-metadata": "^6.1.0", + "gtoken": "^7.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/google-gax": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-4.4.1.tgz", + "integrity": "sha512-Phyp9fMfA00J3sZbJxbbB4jC55b7DBjE3F6poyL3wKMEBVKA79q6BGuHcTiM28yOzVql0NDbRL8MLLh8Iwk9Dg==", + "license": "Apache-2.0", + "dependencies": { + "@grpc/grpc-js": "^1.10.9", + "@grpc/proto-loader": "^0.7.13", + "@types/long": "^4.0.0", + "abort-controller": "^3.0.0", + "duplexify": "^4.0.0", + "google-auth-library": "^9.3.0", + "node-fetch": "^2.7.0", + "object-hash": "^3.0.0", + "proto3-json-serializer": "^2.0.2", + "protobufjs": "^7.3.2", + "retry-request": "^7.0.0", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/google-logging-utils": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-0.0.2.tgz", + "integrity": "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "license": "ISC" + }, + "node_modules/gray-matter": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz", + "integrity": "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-yaml": "^3.13.1", + "kind-of": "^6.0.2", + "section-matter": "^1.0.0", + "strip-bom-string": "^1.0.0" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/gtoken": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz", + "integrity": "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==", + "license": "MIT", + "dependencies": { + "gaxios": "^6.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/handlebars/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==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.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==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hast-util-to-html": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.4.tgz", + "integrity": "sha512-wxQzXtdbhiwGAUKrnQJXlOPmHnEehzphwkK7aluUPQ+lEc1xefC8pblMgpp2w5ldBTEfveRIrADcrhGIWrlTDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-whitespace": "^3.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0", + "stringify-entities": "^4.0.0", + "zwitch": "^2.0.4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/header-case": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/header-case/-/header-case-2.0.4.tgz", + "integrity": "sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q==", + "license": "MIT", + "dependencies": { + "capital-case": "^1.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/heap-js": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/heap-js/-/heap-js-2.6.0.tgz", + "integrity": "sha512-trFMIq3PATiFRiQmNNeHtsrkwYRByIXUbYNbotiY9RLVfMkdwZdd2eQ38mGt7BRiCKBaj1DyBAIHmm7mmXPuuw==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/helmet": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/helmet/-/helmet-8.0.0.tgz", + "integrity": "sha512-VyusHLEIIO5mjQPUI1wpOAEu+wl6Q0998jzTxqUYGE45xCIcAxy3MsbEK/yyJUJ3ADeMoB6MornPH6GMWAf+Pw==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/help-me": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz", + "integrity": "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==", + "license": "MIT" + }, + "node_modules/homedir-polyfill": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", + "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", + "license": "MIT", + "dependencies": { + "parse-passwd": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/hono": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.7.0.tgz", + "integrity": "sha512-hV97aIR4WYbG30k234sD9B3VNr1ZWdQRmrVF76LKFlmI7O9Yo70mG9+mFwyQ6Sjrz4wH71GfnBxv6CPjcx3QNw==", + "license": "MIT", + "engines": { + "node": ">=16.9.0" + } + }, + "node_modules/hookable": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz", + "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", + "license": "MIT" + }, + "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, + "license": "MIT" + }, + "node_modules/html-void-elements": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", + "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=16.17.0" + } + }, + "node_modules/hyperdyperid": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/hyperdyperid/-/hyperdyperid-1.2.0.tgz", + "integrity": "sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==", + "license": "MIT", + "engines": { + "node": ">=10.18" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-in-the-middle": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.13.0.tgz", + "integrity": "sha512-YG86SYDtrL/Yu8JgfWb7kjQ0myLeT1whw6fs/ZHFkXFcbk9zJU9lOCsSJHpvaPumU11nN3US7NW6x1YTk+HrUA==", + "license": "Apache-2.0", + "dependencies": { + "acorn": "^8.14.0", + "acorn-import-attributes": "^1.9.5", + "cjs-module-lexer": "^1.2.2", + "module-details-from-path": "^1.0.3" + } + }, + "node_modules/indent-string": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", + "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "license": "ISC", + "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==", + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "license": "ISC" + }, + "node_modules/inquirer": { + "version": "9.3.7", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-9.3.7.tgz", + "integrity": "sha512-LJKFHCSeIRq9hanN14IlOtPSTe3lNES7TYDTE2xxdAy1LS5rYphajK1qtwvj3YmQXvvk0U2Vbmcni8P9EIQW9w==", + "license": "MIT", + "dependencies": { + "@inquirer/figures": "^1.0.3", + "ansi-escapes": "^4.3.2", + "cli-width": "^4.1.0", + "external-editor": "^3.1.0", + "mute-stream": "1.0.0", + "ora": "^5.4.1", + "run-async": "^3.0.0", + "rxjs": "^7.8.1", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^6.2.0", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/inquirer/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==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/inquirer/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==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/inquirer/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "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/inquirer/node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "license": "MIT", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/inquirer/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==", + "license": "MIT" + }, + "node_modules/inquirer/node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/inquirer/node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/inquirer/node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/inquirer/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==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/inquirer/node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/inquirer/node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "license": "MIT", + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/inquirer/node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "license": "MIT", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/inquirer/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==", + "license": "ISC" + }, + "node_modules/inquirer/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==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/inquirer/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==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/inquirer/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/interpret": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", + "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==", + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/ipaddr.js": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", + "integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/is-absolute": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", + "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", + "license": "MIT", + "dependencies": { + "is-relative": "^1.0.0", + "is-windows": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-arguments": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz", + "integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-function": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", + "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-proto": "^1.0.0", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-interactive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", + "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-3.0.0.tgz", + "integrity": "sha512-kyiNFFLU0Ampr6SDZitD/DwUo4Zs1nSdnygUBqsu3LooL00Qvb5j+UnvApUn/TTj1J3OuE6BTdQ5rudKmU2ZaA==", + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-path-inside": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-4.0.0.tgz", + "integrity": "sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-relative": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", + "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", + "license": "MIT", + "dependencies": { + "is-unc-path": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-unc-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", + "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", + "license": "MIT", + "dependencies": { + "unc-path-regex": "^0.1.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-what": { + "version": "4.1.16", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.16.tgz", + "integrity": "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "license": "MIT", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/isbinaryfile": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-5.0.4.tgz", + "integrity": "sha512-YKBKVkKhty7s8rxddb40oOkuP0NbaeXrQvLin6QMHL7Ypiy2RW9LwOVrVgZRyOrhQlayMd9t+D8yDy8MKFTSDQ==", + "license": "MIT", + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/gjtorikian/" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", + "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.23", + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jackspeak": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.2.tgz", + "integrity": "sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.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==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/joycon": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", + "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/js-sdsl": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz", + "integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, + "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, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/js-yaml/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "license": "MIT", + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, + "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==", + "license": "MIT" + }, + "node_modules/json-schema-ref-resolver": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/json-schema-ref-resolver/-/json-schema-ref-resolver-2.0.1.tgz", + "integrity": "sha512-HG0SIB9X4J8bwbxCbnd5FfPEbcXAJYTi1pBJeP/QPON+w8ovSME8iRG+ElHNxZNX2Qh6eYn1GdzJFS4cDFfx0Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "dequal": "^2.0.3" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "license": "MIT", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jsonwebtoken/node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jsonwebtoken/node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "license": "MIT", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/just-extend": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-6.2.0.tgz", + "integrity": "sha512-cYofQu2Xpom82S6qD778jBDpwvvy39s1l/hrYij2u9AMdQcGRpaBu6kY4mVhuno5kJVi1DAz4aiphA2WI1/OAw==", + "dev": true, + "license": "MIT" + }, + "node_modules/jwa": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", + "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "license": "MIT", + "dependencies": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lazystream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", + "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "^2.0.5" + }, + "engines": { + "node": ">= 0.6.3" + } + }, + "node_modules/lazystream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/lazystream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/lazystream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/liftoff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-4.0.0.tgz", + "integrity": "sha512-rMGwYF8q7g2XhG2ulBmmJgWv25qBsqRbDn5gH0+wnuyeFt7QBJlHJmtg5qEdn4pN6WVAUMgXnIxytMFRX9c1aA==", + "license": "MIT", + "dependencies": { + "extend": "^3.0.2", + "findup-sync": "^5.0.0", + "fined": "^2.0.0", + "flagged-respawn": "^2.0.0", + "is-plain-object": "^5.0.0", + "object.map": "^1.0.1", + "rechoir": "^0.8.0", + "resolve": "^1.20.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/light-my-request": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/light-my-request/-/light-my-request-6.5.1.tgz", + "integrity": "sha512-0q82RyxIextuDtkA0UDofhPHIiQ2kmpa7fwElCSlm/8nQl36cDU1Cw+CAO90Es0lReH2HChClKL84I86Nc52hg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause", + "dependencies": { + "cookie": "^1.0.1", + "process-warning": "^4.0.0", + "set-cookie-parser": "^2.6.0" + } + }, + "node_modules/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "uc.micro": "^2.0.0" + } + }, + "node_modules/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "license": "MIT", + "engines": { + "node": ">=6.11.5" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "license": "MIT" + }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", + "deprecated": "This package is deprecated. Use the optional chaining (?.) operator instead.", + "license": "MIT" + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "license": "MIT" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "license": "MIT" + }, + "node_modules/log-symbols": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz", + "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==", + "license": "MIT", + "dependencies": { + "chalk": "^5.3.0", + "is-unicode-supported": "^1.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols/node_modules/is-unicode-supported": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", + "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/long": { + "version": "5.2.5", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.5.tgz", + "integrity": "sha512-e0r9YBBgNCq1D1o5Dp8FMH0N5hsFtXDBiVa0qoJPHpakvZkmDKPRoGffZJII/XsHvj9An9blm+cRJ01yQqU+Dw==", + "license": "Apache-2.0" + }, + "node_modules/loupe": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.3.tgz", + "integrity": "sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==", + "dev": true, + "license": "MIT" + }, + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/lru-cache": { + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.2.tgz", + "integrity": "sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA==", + "license": "ISC", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/lunr": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", + "dev": true, + "license": "MIT" + }, + "node_modules/magic-string": { + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/magicast": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.5.tgz", + "integrity": "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.25.4", + "@babel/types": "^7.25.4", + "source-map-js": "^1.2.0" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "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, + "license": "ISC" + }, + "node_modules/make-iterator": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz", + "integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==", + "license": "MIT", + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mark.js": { + "version": "8.11.1", + "resolved": "https://registry.npmjs.org/mark.js/-/mark.js-8.11.1.tgz", + "integrity": "sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/markdown-it": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", + "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1", + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz", + "integrity": "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "dev": true, + "license": "MIT" + }, + "node_modules/memfs": { + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.17.0.tgz", + "integrity": "sha512-4eirfZ7thblFmqFjywlTmuWVSvccHAJbn1r8qQLzmTO11qcqpohOjmY2mFce6x7x7WtskzRqApPD0hv+Oa74jg==", + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/json-pack": "^1.0.3", + "@jsonjoy.com/util": "^1.3.0", + "tree-dump": "^1.0.1", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">= 4.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + } + }, + "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==", + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-types": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.1.tgz", + "integrity": "sha512-534m2WhVTddrcKVepwmVEVnUAmtrx9bfIjNoQHRqfnvdaHQiFytEhJoTgpWJvDEXCO5gLTQh3wYC1PgOJA4NSQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/mime": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", + "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/mime-db": { + "version": "1.53.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.53.0.tgz", + "integrity": "sha512-oHlN/w+3MQ3rba9rqFr6V/ypF10LSkdwUysQL7GkXoTgIWeV+tcXGA852TBxH+gsh8UWoyhR1hKcoMJTuWflpg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", + "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minisearch": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/minisearch/-/minisearch-7.1.1.tgz", + "integrity": "sha512-b3YZEYCEH4EdCAtYP7OlDyx7FdPwNzuNwLQ34SfJpM9dlbBZzeXndGavTrC+VCiRWomL21SWfMc6SCKO/U2ZNw==", + "dev": true, + "license": "MIT" + }, + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", + "dev": true, + "license": "MIT" + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "dev": true, + "license": "MIT" + }, + "node_modules/mnemonist": { + "version": "0.39.8", + "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.39.8.tgz", + "integrity": "sha512-vyWo2K3fjrUw8YeeZ1zF0fy6Mu59RHokURlld8ymdUPjMlD9EC9ov1/YPqTgqRvUN9nTr3Gqfz29LYAmu0PHPQ==", + "license": "MIT", + "dependencies": { + "obliterator": "^2.0.1" + } + }, + "node_modules/module-details-from-path": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.3.tgz", + "integrity": "sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==", + "license": "MIT" + }, + "node_modules/mqtt": { + "version": "5.10.3", + "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-5.10.3.tgz", + "integrity": "sha512-hA/6YrUS4fywhBGCjH/XXUuLeueJiPqruVVWjK2A24Ma4KcWfZ/x8x07aoesBV+HXDWBC08tbT4IWfSXNW0Jtw==", + "license": "MIT", + "dependencies": { + "@types/readable-stream": "^4.0.5", + "@types/ws": "^8.5.9", + "commist": "^3.2.0", + "concat-stream": "^2.0.0", + "debug": "^4.3.4", + "help-me": "^5.0.0", + "lru-cache": "^10.0.1", + "minimist": "^1.2.8", + "mqtt-packet": "^9.0.1", + "number-allocator": "^1.0.14", + "readable-stream": "^4.4.2", + "reinterval": "^1.1.0", + "rfdc": "^1.3.0", + "split2": "^4.2.0", + "worker-timers": "^7.1.4", + "ws": "^8.17.1" + }, + "bin": { + "mqtt": "build/bin/mqtt.js", + "mqtt_pub": "build/bin/pub.js", + "mqtt_sub": "build/bin/sub.js" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/mqtt-packet": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-9.0.1.tgz", + "integrity": "sha512-koZF1V/X2RZUI6uD9wN5OK1JxxcG1ofAR4H3LjCw1FkeKzruZQ26aAA6v2m1lZyWONZIR5wMMJFrZJDRNzbiQw==", + "license": "MIT", + "dependencies": { + "bl": "^6.0.8", + "debug": "^4.3.4", + "process-nextick-args": "^2.0.1" + } + }, + "node_modules/mqtt-packet/node_modules/bl": { + "version": "6.0.19", + "resolved": "https://registry.npmjs.org/bl/-/bl-6.0.19.tgz", + "integrity": "sha512-4Ay3A3oDfGg3GGirhl4s62ebtnk0pJZA5mLp672MPKOQXsWvXjEF4dqdXySjJIs7b9OVr/O8aOo0Lm+xdjo2JA==", + "license": "MIT", + "dependencies": { + "@types/readable-stream": "^4.0.0", + "buffer": "^6.0.3", + "inherits": "^2.0.4", + "readable-stream": "^4.2.0" + } + }, + "node_modules/mqtt/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, + "node_modules/mrmime": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz", + "integrity": "sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/mute-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", + "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/nan": { + "version": "2.22.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.22.0.tgz", + "integrity": "sha512-nbajikzWTMwsW+eSsNm3QwlOs7het9gGJU5dDZzRTQGk03vyBOauxgI4VakDzE0PtsGTmXPsXTbbjVhRwR5mpw==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/nanoid": { + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/nats": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/nats/-/nats-2.29.1.tgz", + "integrity": "sha512-OHVsxrQCITTdMKG3So0jhtnBd5jS2u1xpS91UCws7VklsaCbctwg5vT/8lYpVldPW0x3aHGF8uuAoMfCoJy7Sg==", + "license": "Apache-2.0", + "dependencies": { + "nkeys.js": "1.1.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "license": "MIT" + }, + "node_modules/nise": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/nise/-/nise-6.1.1.tgz", + "integrity": "sha512-aMSAzLVY7LyeM60gvBS423nBmIPP+Wy7St7hsb+8/fc1HmeoHJfLO8CKse4u3BtOZvQLJghYPI2i/1WZrEj5/g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.1", + "@sinonjs/fake-timers": "^13.0.1", + "@sinonjs/text-encoding": "^0.7.3", + "just-extend": "^6.2.0", + "path-to-regexp": "^8.1.0" + } + }, + "node_modules/nkeys.js": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/nkeys.js/-/nkeys.js-1.1.0.tgz", + "integrity": "sha512-tB/a0shZL5UZWSwsoeyqfTszONTt4k2YS0tuQioMOD180+MbombYVgzDUYHlx+gejYK6rgf08n/2Df99WY0Sxg==", + "license": "Apache-2.0", + "dependencies": { + "tweetnacl": "1.0.3" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/nkeys.js/node_modules/tweetnacl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==", + "license": "Unlicense" + }, + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "license": "MIT", + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-plop": { + "version": "0.32.0", + "resolved": "https://registry.npmjs.org/node-plop/-/node-plop-0.32.0.tgz", + "integrity": "sha512-lKFSRSRuDHhwDKMUobdsvaWCbbDRbV3jMUSMiajQSQux1aNUevAZVxUHc2JERI//W8ABPRbi3ebYuSuIzkNIpQ==", + "license": "MIT", + "dependencies": { + "@types/inquirer": "^9.0.3", + "change-case": "^4.1.2", + "del": "^7.1.0", + "globby": "^13.2.2", + "handlebars": "^4.7.8", + "inquirer": "^9.2.10", + "isbinaryfile": "^5.0.0", + "lodash.get": "^4.4.2", + "lower-case": "^2.0.2", + "mkdirp": "^3.0.1", + "resolve": "^1.22.4", + "title-case": "^3.0.3", + "upper-case": "^2.0.2" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/node-plop/node_modules/mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/npm/-/npm-11.1.0.tgz", + "integrity": "sha512-rPMBrZud26lI/LcjQeLw/K5Hf1apXMKgkpNNEzp0YQYmM877+T1ZNKPcB2hnTi7e6fBNz8xLtMMn/w46fVUqGw==", + "bundleDependencies": [ + "@isaacs/string-locale-compare", + "@npmcli/arborist", + "@npmcli/config", + "@npmcli/fs", + "@npmcli/map-workspaces", + "@npmcli/package-json", + "@npmcli/promise-spawn", + "@npmcli/redact", + "@npmcli/run-script", + "@sigstore/tuf", + "abbrev", + "archy", + "cacache", + "chalk", + "ci-info", + "cli-columns", + "fastest-levenshtein", + "fs-minipass", + "glob", + "graceful-fs", + "hosted-git-info", + "ini", + "init-package-json", + "is-cidr", + "json-parse-even-better-errors", + "libnpmaccess", + "libnpmdiff", + "libnpmexec", + "libnpmfund", + "libnpmorg", + "libnpmpack", + "libnpmpublish", + "libnpmsearch", + "libnpmteam", + "libnpmversion", + "make-fetch-happen", + "minimatch", + "minipass", + "minipass-pipeline", + "ms", + "node-gyp", + "nopt", + "normalize-package-data", + "npm-audit-report", + "npm-install-checks", + "npm-package-arg", + "npm-pick-manifest", + "npm-profile", + "npm-registry-fetch", + "npm-user-validate", + "p-map", + "pacote", + "parse-conflict-json", + "proc-log", + "qrcode-terminal", + "read", + "semver", + "spdx-expression-parse", + "ssri", + "supports-color", + "tar", + "text-table", + "tiny-relative-date", + "treeverse", + "validate-npm-package-name", + "which" + ], + "license": "Artistic-2.0", + "workspaces": [ + "docs", + "smoke-tests", + "mock-globals", + "mock-registry", + "workspaces/*" + ], + "dependencies": { + "@isaacs/string-locale-compare": "^1.1.0", + "@npmcli/arborist": "^9.0.0", + "@npmcli/config": "^10.0.1", + "@npmcli/fs": "^4.0.0", + "@npmcli/map-workspaces": "^4.0.2", + "@npmcli/package-json": "^6.1.1", + "@npmcli/promise-spawn": "^8.0.2", + "@npmcli/redact": "^3.0.0", + "@npmcli/run-script": "^9.0.1", + "@sigstore/tuf": "^3.0.0", + "abbrev": "^3.0.0", + "archy": "~1.0.0", + "cacache": "^19.0.1", + "chalk": "^5.4.1", + "ci-info": "^4.1.0", + "cli-columns": "^4.0.0", + "fastest-levenshtein": "^1.0.16", + "fs-minipass": "^3.0.3", + "glob": "^10.4.5", + "graceful-fs": "^4.2.11", + "hosted-git-info": "^8.0.2", + "ini": "^5.0.0", + "init-package-json": "^8.0.0", + "is-cidr": "^5.1.0", + "json-parse-even-better-errors": "^4.0.0", + "libnpmaccess": "^10.0.0", + "libnpmdiff": "^8.0.0", + "libnpmexec": "^10.0.0", + "libnpmfund": "^7.0.0", + "libnpmorg": "^8.0.0", + "libnpmpack": "^9.0.0", + "libnpmpublish": "^11.0.0", + "libnpmsearch": "^9.0.0", + "libnpmteam": "^8.0.0", + "libnpmversion": "^8.0.0", + "make-fetch-happen": "^14.0.3", + "minimatch": "^9.0.5", + "minipass": "^7.1.1", + "minipass-pipeline": "^1.2.4", + "ms": "^2.1.2", + "node-gyp": "^11.0.0", + "nopt": "^8.0.0", + "normalize-package-data": "^7.0.0", + "npm-audit-report": "^6.0.0", + "npm-install-checks": "^7.1.1", + "npm-package-arg": "^12.0.1", + "npm-pick-manifest": "^10.0.0", + "npm-profile": "^11.0.1", + "npm-registry-fetch": "^18.0.2", + "npm-user-validate": "^3.0.0", + "p-map": "^7.0.3", + "pacote": "^21.0.0", + "parse-conflict-json": "^4.0.0", + "proc-log": "^5.0.0", + "qrcode-terminal": "^0.12.0", + "read": "^4.0.0", + "semver": "^7.6.3", + "spdx-expression-parse": "^4.0.0", + "ssri": "^12.0.0", + "supports-color": "^9.4.0", + "tar": "^6.2.1", + "text-table": "~0.2.0", + "tiny-relative-date": "^1.3.0", + "treeverse": "^3.0.0", + "validate-npm-package-name": "^6.0.0", + "which": "^5.0.0" + }, + "bin": { + "npm": "bin/npm-cli.js", + "npx": "bin/npx-cli.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm-check-updates": { + "version": "17.1.14", + "resolved": "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-17.1.14.tgz", + "integrity": "sha512-dr4bXIxETubLI1tFGeock5hN8yVjahvaVpx+lPO4/O2md3zJuxB7FgH3MIoTvQSCgsgkIRpe0skti01IEAA5tA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "ncu": "build/cli.js", + "npm-check-updates": "build/cli.js" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0", + "npm": ">=8.12.1" + } + }, + "node_modules/npm-run-path": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui": { + "version": "8.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/npm/node_modules/@isaacs/string-locale-compare": { + "version": "1.1.0", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/@npmcli/agent": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "agent-base": "^7.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.1", + "lru-cache": "^10.0.1", + "socks-proxy-agent": "^8.0.3" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/arborist": { + "version": "9.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@isaacs/string-locale-compare": "^1.1.0", + "@npmcli/fs": "^4.0.0", + "@npmcli/installed-package-contents": "^3.0.0", + "@npmcli/map-workspaces": "^4.0.1", + "@npmcli/metavuln-calculator": "^9.0.0", + "@npmcli/name-from-folder": "^3.0.0", + "@npmcli/node-gyp": "^4.0.0", + "@npmcli/package-json": "^6.0.1", + "@npmcli/query": "^4.0.0", + "@npmcli/redact": "^3.0.0", + "@npmcli/run-script": "^9.0.1", + "bin-links": "^5.0.0", + "cacache": "^19.0.1", + "common-ancestor-path": "^1.0.1", + "hosted-git-info": "^8.0.0", + "json-stringify-nice": "^1.1.4", + "lru-cache": "^10.2.2", + "minimatch": "^9.0.4", + "nopt": "^8.0.0", + "npm-install-checks": "^7.1.0", + "npm-package-arg": "^12.0.0", + "npm-pick-manifest": "^10.0.0", + "npm-registry-fetch": "^18.0.1", + "pacote": "^21.0.0", + "parse-conflict-json": "^4.0.0", + "proc-log": "^5.0.0", + "proggy": "^3.0.0", + "promise-all-reject-late": "^1.0.0", + "promise-call-limit": "^3.0.1", + "read-package-json-fast": "^4.0.0", + "semver": "^7.3.7", + "ssri": "^12.0.0", + "treeverse": "^3.0.0", + "walk-up-path": "^4.0.0" + }, + "bin": { + "arborist": "bin/index.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/@npmcli/config": { + "version": "10.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/map-workspaces": "^4.0.1", + "@npmcli/package-json": "^6.0.1", + "ci-info": "^4.0.0", + "ini": "^5.0.0", + "nopt": "^8.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5", + "walk-up-path": "^4.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/@npmcli/fs": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/git": { + "version": "6.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/promise-spawn": "^8.0.0", + "ini": "^5.0.0", + "lru-cache": "^10.0.1", + "npm-pick-manifest": "^10.0.0", + "proc-log": "^5.0.0", + "promise-inflight": "^1.0.1", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/installed-package-contents": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-bundled": "^4.0.0", + "npm-normalize-package-bin": "^4.0.0" + }, + "bin": { + "installed-package-contents": "bin/index.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/map-workspaces": { + "version": "4.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/name-from-folder": "^3.0.0", + "@npmcli/package-json": "^6.0.0", + "glob": "^10.2.2", + "minimatch": "^9.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/metavuln-calculator": { + "version": "9.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "cacache": "^19.0.0", + "json-parse-even-better-errors": "^4.0.0", + "pacote": "^21.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/@npmcli/name-from-folder": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/node-gyp": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/package-json": { + "version": "6.1.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^6.0.0", + "glob": "^10.2.2", + "hosted-git-info": "^8.0.0", + "json-parse-even-better-errors": "^4.0.0", + "proc-log": "^5.0.0", + "semver": "^7.5.3", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/promise-spawn": { + "version": "8.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "which": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/query": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "postcss-selector-parser": "^6.1.2" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/redact": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/run-script": { + "version": "9.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/node-gyp": "^4.0.0", + "@npmcli/package-json": "^6.0.0", + "@npmcli/promise-spawn": "^8.0.0", + "node-gyp": "^11.0.0", + "proc-log": "^5.0.0", + "which": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "inBundle": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/npm/node_modules/@sigstore/bundle": { + "version": "3.0.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/protobuf-specs": "^0.3.2" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@sigstore/core": { + "version": "2.0.0", + "inBundle": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@sigstore/protobuf-specs": { + "version": "0.3.3", + "inBundle": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@sigstore/sign": { + "version": "3.0.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^3.0.0", + "@sigstore/core": "^2.0.0", + "@sigstore/protobuf-specs": "^0.3.2", + "make-fetch-happen": "^14.0.1", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@sigstore/tuf": { + "version": "3.0.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/protobuf-specs": "^0.3.2", + "tuf-js": "^3.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@sigstore/verify": { + "version": "2.0.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^3.0.0", + "@sigstore/core": "^2.0.0", + "@sigstore/protobuf-specs": "^0.3.2" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@tufjs/canonical-json": { + "version": "2.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@tufjs/models": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "@tufjs/canonical-json": "2.0.0", + "minimatch": "^9.0.5" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/abbrev": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/agent-base": { + "version": "7.1.3", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/ansi-regex": { + "version": "5.0.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/ansi-styles": { + "version": "6.2.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/npm/node_modules/aproba": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/archy": { + "version": "1.0.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/balanced-match": { + "version": "1.0.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/bin-links": { + "version": "5.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "cmd-shim": "^7.0.0", + "npm-normalize-package-bin": "^4.0.0", + "proc-log": "^5.0.0", + "read-cmd-shim": "^5.0.0", + "write-file-atomic": "^6.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/binary-extensions": { + "version": "3.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=18.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/brace-expansion": { + "version": "2.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/npm/node_modules/cacache": { + "version": "19.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/fs": "^4.0.0", + "fs-minipass": "^3.0.0", + "glob": "^10.2.2", + "lru-cache": "^10.0.1", + "minipass": "^7.0.3", + "minipass-collect": "^2.0.1", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^7.0.2", + "ssri": "^12.0.0", + "tar": "^7.4.3", + "unique-filename": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/cacache/node_modules/chownr": { + "version": "3.0.0", + "inBundle": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/cacache/node_modules/minizlib": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.4", + "rimraf": "^5.0.5" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/npm/node_modules/cacache/node_modules/mkdirp": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/cacache/node_modules/tar": { + "version": "7.4.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/cacache/node_modules/yallist": { + "version": "5.0.0", + "inBundle": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/chalk": { + "version": "5.4.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/npm/node_modules/chownr": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/ci-info": { + "version": "4.1.0", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/cidr-regex": { + "version": "4.1.1", + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "ip-regex": "^5.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/npm/node_modules/cli-columns": { + "version": "4.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/npm/node_modules/cmd-shim": { + "version": "7.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/color-convert": { + "version": "2.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/npm/node_modules/color-name": { + "version": "1.1.4", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/common-ancestor-path": { + "version": "1.0.1", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/cross-spawn": { + "version": "7.0.6", + "inBundle": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/cross-spawn/node_modules/which": { + "version": "2.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/cssesc": { + "version": "3.0.0", + "inBundle": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm/node_modules/debug": { + "version": "4.4.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/npm/node_modules/diff": { + "version": "7.0.0", + "inBundle": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/npm/node_modules/eastasianwidth": { + "version": "0.2.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/emoji-regex": { + "version": "8.0.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/encoding": { + "version": "0.1.13", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/npm/node_modules/env-paths": { + "version": "2.2.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/npm/node_modules/err-code": { + "version": "2.0.3", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/exponential-backoff": { + "version": "3.1.1", + "inBundle": true, + "license": "Apache-2.0" + }, + "node_modules/npm/node_modules/fastest-levenshtein": { + "version": "1.0.16", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 4.9.1" + } + }, + "node_modules/npm/node_modules/foreground-child": { + "version": "3.3.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/fs-minipass": { + "version": "3.0.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/glob": { + "version": "10.4.5", + "inBundle": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/graceful-fs": { + "version": "4.2.11", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/hosted-git-info": { + "version": "8.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/http-cache-semantics": { + "version": "4.1.1", + "inBundle": true, + "license": "BSD-2-Clause" + }, + "node_modules/npm/node_modules/http-proxy-agent": { + "version": "7.0.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/https-proxy-agent": { + "version": "7.0.6", + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/iconv-lite": { + "version": "0.6.3", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm/node_modules/ignore-walk": { + "version": "7.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minimatch": "^9.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/imurmurhash": { + "version": "0.1.4", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/npm/node_modules/ini": { + "version": "5.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/init-package-json": { + "version": "8.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/package-json": "^6.1.0", + "npm-package-arg": "^12.0.0", + "promzard": "^2.0.0", + "read": "^4.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4", + "validate-npm-package-name": "^6.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/ip-address": { + "version": "9.0.5", + "inBundle": true, + "license": "MIT", + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/npm/node_modules/ip-regex": { + "version": "5.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/is-cidr": { + "version": "5.1.0", + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "cidr-regex": "^4.1.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/npm/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/isexe": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/jackspeak": { + "version": "3.4.3", + "inBundle": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/npm/node_modules/jsbn": { + "version": "1.1.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/json-parse-even-better-errors": { + "version": "4.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/json-stringify-nice": { + "version": "1.1.4", + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/jsonparse": { + "version": "1.3.1", + "engines": [ + "node >= 0.2.0" + ], + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/just-diff": { + "version": "6.0.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/just-diff-apply": { + "version": "5.5.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/libnpmaccess": { + "version": "10.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-package-arg": "^12.0.0", + "npm-registry-fetch": "^18.0.1" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/libnpmdiff": { + "version": "8.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^9.0.0", + "@npmcli/installed-package-contents": "^3.0.0", + "binary-extensions": "^3.0.0", + "diff": "^7.0.0", + "minimatch": "^9.0.4", + "npm-package-arg": "^12.0.0", + "pacote": "^21.0.0", + "tar": "^6.2.1" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/libnpmexec": { + "version": "10.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^9.0.0", + "@npmcli/run-script": "^9.0.1", + "ci-info": "^4.0.0", + "npm-package-arg": "^12.0.0", + "pacote": "^21.0.0", + "proc-log": "^5.0.0", + "read": "^4.0.0", + "read-package-json-fast": "^4.0.0", + "semver": "^7.3.7", + "walk-up-path": "^4.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/libnpmfund": { + "version": "7.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^9.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/libnpmorg": { + "version": "8.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^18.0.1" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/libnpmpack": { + "version": "9.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^9.0.0", + "@npmcli/run-script": "^9.0.1", + "npm-package-arg": "^12.0.0", + "pacote": "^21.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/libnpmpublish": { + "version": "11.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "ci-info": "^4.0.0", + "normalize-package-data": "^7.0.0", + "npm-package-arg": "^12.0.0", + "npm-registry-fetch": "^18.0.1", + "proc-log": "^5.0.0", + "semver": "^7.3.7", + "sigstore": "^3.0.0", + "ssri": "^12.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/libnpmsearch": { + "version": "9.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-registry-fetch": "^18.0.1" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/libnpmteam": { + "version": "8.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^18.0.1" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/libnpmversion": { + "version": "8.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^6.0.1", + "@npmcli/run-script": "^9.0.1", + "json-parse-even-better-errors": "^4.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.7" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/lru-cache": { + "version": "10.4.3", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/make-fetch-happen": { + "version": "14.0.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/agent": "^3.0.0", + "cacache": "^19.0.1", + "http-cache-semantics": "^4.1.1", + "minipass": "^7.0.2", + "minipass-fetch": "^4.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^1.0.0", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1", + "ssri": "^12.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/make-fetch-happen/node_modules/negotiator": { + "version": "1.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/npm/node_modules/minimatch": { + "version": "9.0.5", + "inBundle": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/minipass": { + "version": "7.1.2", + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/npm/node_modules/minipass-collect": { + "version": "2.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/npm/node_modules/minipass-fetch": { + "version": "4.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.3", + "minipass-sized": "^1.0.3", + "minizlib": "^3.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/npm/node_modules/minipass-fetch/node_modules/minizlib": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.4", + "rimraf": "^5.0.5" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/npm/node_modules/minipass-flush": { + "version": "1.0.5", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/minipass-flush/node_modules/minipass": { + "version": "3.3.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-pipeline": { + "version": "1.2.4", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-pipeline/node_modules/minipass": { + "version": "3.3.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-sized": { + "version": "1.0.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-sized/node_modules/minipass": { + "version": "3.3.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minizlib": { + "version": "2.1.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/mkdirp": { + "version": "1.0.4", + "inBundle": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/ms": { + "version": "2.1.3", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/mute-stream": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/node-gyp": { + "version": "11.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.0", + "exponential-backoff": "^3.1.1", + "glob": "^10.3.10", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^14.0.3", + "nopt": "^8.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5", + "tar": "^7.4.3", + "which": "^5.0.0" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/chownr": { + "version": "3.0.0", + "inBundle": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/minizlib": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.4", + "rimraf": "^5.0.5" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/mkdirp": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/tar": { + "version": "7.4.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/yallist": { + "version": "5.0.0", + "inBundle": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/nopt": { + "version": "8.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "abbrev": "^2.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/nopt/node_modules/abbrev": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/normalize-package-data": { + "version": "7.0.0", + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^8.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-audit-report": { + "version": "6.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-bundled": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-normalize-package-bin": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-install-checks": { + "version": "7.1.1", + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "semver": "^7.1.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-normalize-package-bin": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-package-arg": { + "version": "12.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "hosted-git-info": "^8.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^6.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-packlist": { + "version": "10.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "ignore-walk": "^7.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/npm-pick-manifest": { + "version": "10.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-install-checks": "^7.1.0", + "npm-normalize-package-bin": "^4.0.0", + "npm-package-arg": "^12.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-profile": { + "version": "11.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-registry-fetch": "^18.0.0", + "proc-log": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-registry-fetch": { + "version": "18.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/redact": "^3.0.0", + "jsonparse": "^1.3.1", + "make-fetch-happen": "^14.0.0", + "minipass": "^7.0.2", + "minipass-fetch": "^4.0.0", + "minizlib": "^3.0.1", + "npm-package-arg": "^12.0.0", + "proc-log": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-registry-fetch/node_modules/minizlib": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.4", + "rimraf": "^5.0.5" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/npm/node_modules/npm-user-validate": { + "version": "3.0.0", + "inBundle": true, + "license": "BSD-2-Clause", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/p-map": { + "version": "7.0.3", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/package-json-from-dist": { + "version": "1.0.1", + "inBundle": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/npm/node_modules/pacote": { + "version": "21.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^6.0.0", + "@npmcli/installed-package-contents": "^3.0.0", + "@npmcli/package-json": "^6.0.0", + "@npmcli/promise-spawn": "^8.0.0", + "@npmcli/run-script": "^9.0.0", + "cacache": "^19.0.0", + "fs-minipass": "^3.0.0", + "minipass": "^7.0.2", + "npm-package-arg": "^12.0.0", + "npm-packlist": "^10.0.0", + "npm-pick-manifest": "^10.0.0", + "npm-registry-fetch": "^18.0.0", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1", + "sigstore": "^3.0.0", + "ssri": "^12.0.0", + "tar": "^6.1.11" + }, + "bin": { + "pacote": "bin/index.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/parse-conflict-json": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^4.0.0", + "just-diff": "^6.0.0", + "just-diff-apply": "^5.2.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/path-key": { + "version": "3.1.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/path-scurry": { + "version": "1.11.1", + "inBundle": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/postcss-selector-parser": { + "version": "6.1.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm/node_modules/proc-log": { + "version": "5.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/proggy": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/promise-all-reject-late": { + "version": "1.0.1", + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/promise-call-limit": { + "version": "3.0.2", + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/promise-inflight": { + "version": "1.0.1", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/promise-retry": { + "version": "2.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/promzard": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "read": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/qrcode-terminal": { + "version": "0.12.0", + "inBundle": true, + "bin": { + "qrcode-terminal": "bin/qrcode-terminal.js" + } + }, + "node_modules/npm/node_modules/read": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "mute-stream": "^2.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/read-cmd-shim": { + "version": "5.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/read-package-json-fast": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^4.0.0", + "npm-normalize-package-bin": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/retry": { + "version": "0.12.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/npm/node_modules/rimraf": { + "version": "5.0.10", + "inBundle": true, + "license": "ISC", + "dependencies": { + "glob": "^10.3.7" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/safer-buffer": { + "version": "2.1.2", + "inBundle": true, + "license": "MIT", + "optional": true + }, + "node_modules/npm/node_modules/semver": { + "version": "7.6.3", + "inBundle": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/shebang-command": { + "version": "2.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/shebang-regex": { + "version": "3.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/signal-exit": { + "version": "4.1.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/sigstore": { + "version": "3.0.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^3.0.0", + "@sigstore/core": "^2.0.0", + "@sigstore/protobuf-specs": "^0.3.2", + "@sigstore/sign": "^3.0.0", + "@sigstore/tuf": "^3.0.0", + "@sigstore/verify": "^2.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/smart-buffer": { + "version": "4.2.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/npm/node_modules/socks": { + "version": "2.8.3", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ip-address": "^9.0.5", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/npm/node_modules/socks-proxy-agent": { + "version": "8.0.5", + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/spdx-correct": { + "version": "3.2.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/spdx-correct/node_modules/spdx-expression-parse": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/spdx-exceptions": { + "version": "2.5.0", + "inBundle": true, + "license": "CC-BY-3.0" + }, + "node_modules/npm/node_modules/spdx-expression-parse": { + "version": "4.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/spdx-license-ids": { + "version": "3.0.21", + "inBundle": true, + "license": "CC0-1.0" + }, + "node_modules/npm/node_modules/sprintf-js": { + "version": "1.1.3", + "inBundle": true, + "license": "BSD-3-Clause" + }, + "node_modules/npm/node_modules/ssri": { + "version": "12.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/string-width": { + "version": "4.2.3", + "inBundle": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "inBundle": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/strip-ansi": { + "version": "6.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/supports-color": { + "version": "9.4.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/npm/node_modules/tar": { + "version": "6.2.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/tar/node_modules/fs-minipass": { + "version": "2.1.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/tar/node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/text-table": { + "version": "0.2.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/tiny-relative-date": { + "version": "1.3.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/treeverse": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/tuf-js": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "@tufjs/models": "3.0.1", + "debug": "^4.3.6", + "make-fetch-happen": "^14.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/unique-filename": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "unique-slug": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/unique-slug": { + "version": "5.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/util-deprecate": { + "version": "1.0.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/validate-npm-package-license": { + "version": "3.0.4", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/npm/node_modules/validate-npm-package-license/node_modules/spdx-expression-parse": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/validate-npm-package-name": { + "version": "6.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/walk-up-path": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/npm/node_modules/which": { + "version": "5.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/which/node_modules/isexe": { + "version": "3.1.1", + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/npm/node_modules/wrap-ansi": { + "version": "8.1.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "inBundle": true, + "license": "MIT", + "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/npm/node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.1.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/emoji-regex": { + "version": "9.2.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/string-width": { + "version": "5.1.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.1.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/write-file-atomic": { + "version": "6.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/yallist": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC" + }, + "node_modules/number-allocator": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/number-allocator/-/number-allocator-1.0.14.tgz", + "integrity": "sha512-OrL44UTVAvkKdOdRQZIJpLkAdjXGTRda052sN4sO77bKEzYYqWKMBjQvrJFzqygI99gL6Z4u2xctPW1tB8ErvA==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.1", + "js-sdsl": "4.3.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/object.defaults": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz", + "integrity": "sha512-c/K0mw/F11k4dEUBMW8naXUuBuhxRCfG7W+yFy8EcijU/rSmazOUd1XAEEe6bC0OuXY4HUKjTJv7xbxIMqdxrA==", + "license": "MIT", + "dependencies": { + "array-each": "^1.0.1", + "array-slice": "^1.0.0", + "for-own": "^1.0.0", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz", + "integrity": "sha512-3+mAJu2PLfnSVGHwIWubpOFLscJANBKuB/6A4CxBstc4aqwQY0FWcsppuy4jU5GSB95yES5JHSI+33AWuS4k6w==", + "license": "MIT", + "dependencies": { + "for-own": "^1.0.0", + "make-iterator": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==", + "license": "MIT", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/obliterator": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-2.0.5.tgz", + "integrity": "sha512-42CPE9AhahZRsMNslczq0ctAEtqk8Eka26QofnqC346BZdHDySk3LWka23LI7ULIw11NmltpiLagIq8gBozxTw==", + "license": "MIT" + }, + "node_modules/on-exit-leak-free": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", + "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/oniguruma-to-es": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-3.1.0.tgz", + "integrity": "sha512-BJ3Jy22YlgejHSO7Fvmz1kKazlaPmRSUH+4adTDUS/dKQ4wLxI+gALZ8updbaux7/m7fIlpgOZ5fp/Inq5jUAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex-xs": "^1.0.0", + "regex": "^6.0.1", + "regex-recursion": "^6.0.2" + } + }, + "node_modules/open": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "license": "MIT", + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/openapi3-ts": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/openapi3-ts/-/openapi3-ts-4.4.0.tgz", + "integrity": "sha512-9asTNB9IkKEzWMcHmVZE7Ts3kC9G7AFHfs8i7caD8HbI76gEjdkId4z/AkP83xdZsH7PLAnnbl47qZkXuxpArw==", + "license": "MIT", + "dependencies": { + "yaml": "^2.5.0" + } + }, + "node_modules/ora": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-8.2.0.tgz", + "integrity": "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==", + "license": "MIT", + "dependencies": { + "chalk": "^5.3.0", + "cli-cursor": "^5.0.0", + "cli-spinners": "^2.9.2", + "is-interactive": "^2.0.0", + "is-unicode-supported": "^2.0.0", + "log-symbols": "^6.0.0", + "stdin-discarder": "^0.2.2", + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/emoji-regex": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", + "license": "MIT" + }, + "node_modules/ora/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/p-map": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-5.5.0.tgz", + "integrity": "sha512-VFqfGDHlx87K66yZrNdI4YGtD70IRyd+zSvgks6mzHPRNkoKy+9EKP4SFC77/vTTQYmRmti7dvqC+m5jBrBAcg==", + "license": "MIT", + "dependencies": { + "aggregate-error": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0" + }, + "node_modules/param-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", + "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", + "license": "MIT", + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/parse-filepath": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", + "integrity": "sha512-FwdRXKCohSVeXqwtYonZTXtbGJKrn+HNyWDYVcp5yuJlesTwNH4rsmRZ+GrKAPJ5bLpRxESMeS+Rl0VCHRvB2Q==", + "license": "MIT", + "dependencies": { + "is-absolute": "^1.0.0", + "map-cache": "^0.2.0", + "path-root": "^0.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pascal-case": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", + "license": "MIT", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "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==", + "license": "MIT" + }, + "node_modules/path-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/path-case/-/path-case-3.0.4.tgz", + "integrity": "sha512-qO4qCFjXqVTrcbPt/hQfhTQ+VhFsqNKOPtytgNKkKxSoEp3XPUQ8ObFuePylOIok5gjn69ry8XiULxCwot3Wfg==", + "license": "MIT", + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "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==", + "license": "MIT" + }, + "node_modules/path-root": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz", + "integrity": "sha512-QLcPegTHF11axjfojBIoDygmS2E3Lf+8+jI6wOVmNVenrKSo3mFdSGiIgdSHenczw3wPtlVMQaFVwGmM7BJdtg==", + "license": "MIT", + "dependencies": { + "path-root-regex": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-root-regex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz", + "integrity": "sha512-4GlJ6rZDhQZFE0DPVKh0e9jmZ5egZfxTkp7bcRDuPlJXbAwhxcl2dINPUAsjLdejqaLsCeg8axcLjIbvBjN4pQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-scurry": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", + "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-to-regexp": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", + "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/pathe": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.2.tgz", + "integrity": "sha512-15Ztpk+nov8DR524R4BF7uEuzESgzUEAV4Ah7CUMNGXdE5ELuvxElxGXndBl32vMSsWa1jpNf22Z+Er3sKwq+w==", + "dev": true, + "license": "MIT" + }, + "node_modules/pathval": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz", + "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.16" + } + }, + "node_modules/peek-stream": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/peek-stream/-/peek-stream-1.1.3.tgz", + "integrity": "sha512-FhJ+YbOSBb9/rIl2ZeE/QHEsWn7PqNYt8ARAY3kIgNGOk13g9FGyIY6JIl/xB/3TFRVoTv5as0l11weORrTekA==", + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "duplexify": "^3.5.0", + "through2": "^2.0.3" + } + }, + "node_modules/peek-stream/node_modules/duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, + "node_modules/peek-stream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/peek-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/peek-stream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/perfect-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", + "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", + "dev": true, + "license": "MIT" + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "license": "ISC", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.7.0.tgz", + "integrity": "sha512-hTK/mE36i8fDDhgDFjy6xNOG+LCorxLG3WO17tku+ij6sVHXh1jQUJ8hYAnRhNla4QVD2H8er/FOjc/+EgC6yQ==", + "license": "MIT" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "license": "MIT", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pino": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/pino/-/pino-9.6.0.tgz", + "integrity": "sha512-i85pKRCt4qMjZ1+L7sy2Ag4t1atFcdbEt76+7iRJn1g2BvsnRMGu9p8pivl9fs63M2kF/A0OacFZhTub+m/qMg==", + "license": "MIT", + "dependencies": { + "atomic-sleep": "^1.0.0", + "fast-redact": "^3.1.1", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^2.0.0", + "pino-std-serializers": "^7.0.0", + "process-warning": "^4.0.0", + "quick-format-unescaped": "^4.0.3", + "real-require": "^0.2.0", + "safe-stable-stringify": "^2.3.1", + "sonic-boom": "^4.0.1", + "thread-stream": "^3.0.0" + }, + "bin": { + "pino": "bin.js" + } + }, + "node_modules/pino-abstract-transport": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-2.0.0.tgz", + "integrity": "sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==", + "license": "MIT", + "dependencies": { + "split2": "^4.0.0" + } + }, + "node_modules/pino-loki": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/pino-loki/-/pino-loki-2.5.0.tgz", + "integrity": "sha512-/QCSukecqbjXWcVB58st0bKJFQiXg4ZEGXslnnMCMbcrDr5LT36XvkS0dy0eBvZvI8bWoh3efjLYLmN+94iLSQ==", + "license": "MIT", + "dependencies": { + "commander": "^12.1.0", + "pino-abstract-transport": "^2.0.0", + "pump": "^3.0.2" + }, + "bin": { + "pino-loki": "dist/cli.cjs" + }, + "funding": { + "url": "https://github.com/sponsors/Julien-R44" + } + }, + "node_modules/pino-pretty": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-13.0.0.tgz", + "integrity": "sha512-cQBBIVG3YajgoUjo1FdKVRX6t9XPxwB9lcNJVD5GCnNM4Y6T12YYx8c6zEejxQsU0wrg9TwmDulcE9LR7qcJqA==", + "license": "MIT", + "dependencies": { + "colorette": "^2.0.7", + "dateformat": "^4.6.3", + "fast-copy": "^3.0.2", + "fast-safe-stringify": "^2.1.1", + "help-me": "^5.0.0", + "joycon": "^3.1.1", + "minimist": "^1.2.6", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^2.0.0", + "pump": "^3.0.0", + "secure-json-parse": "^2.4.0", + "sonic-boom": "^4.0.1", + "strip-json-comments": "^3.1.1" + }, + "bin": { + "pino-pretty": "bin.js" + } + }, + "node_modules/pino-pretty/node_modules/secure-json-parse": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz", + "integrity": "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==", + "license": "BSD-3-Clause" + }, + "node_modules/pino-std-serializers": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.0.0.tgz", + "integrity": "sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==", + "license": "MIT" + }, + "node_modules/plop": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/plop/-/plop-4.0.1.tgz", + "integrity": "sha512-5n8QU93kvL/ObOzBcPAB1siVFtAH1TZM6TntJ3JK5kXT0jIgnQV+j+uaOWWFJlg1cNkzLYm8klgASF65K36q9w==", + "license": "MIT", + "dependencies": { + "@types/liftoff": "^4.0.3", + "chalk": "^5.3.0", + "interpret": "^3.1.1", + "liftoff": "^4.0.0", + "minimist": "^1.2.8", + "node-plop": "^0.32.0", + "ora": "^8.0.0", + "v8flags": "^4.0.1" + }, + "bin": { + "plop": "bin/plop.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/polite-json": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/polite-json/-/polite-json-5.0.0.tgz", + "integrity": "sha512-OLS/0XeUAcE8a2fdwemNja+udKgXNnY6yKVIXqAD2zVRx1KvY6Ato/rZ2vdzbxqYwPW0u6SCNC/bAMPNzpzxbw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postcss": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.1.tgz", + "integrity": "sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.8", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "license": "MIT", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/preact": { + "version": "10.25.4", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.25.4.tgz", + "integrity": "sha512-jLdZDb+Q+odkHJ+MpW/9U5cODzqnB+fy2EiHSZES7ldV5LK7yjlVzTp7R8Xy6W6y75kfK8iWYtFVH7lvjwrCMA==", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, + "node_modules/process-warning": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-4.0.1.tgz", + "integrity": "sha512-3c2LzQ3rY9d0hc1emcsHhfT9Jwz0cChib/QN89oME2R451w5fy3f0afAhERFZAwrbDU43wk12d0ORBpDVME50Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" + }, + "node_modules/proper-lockfile": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", + "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "retry": "^0.12.0", + "signal-exit": "^3.0.2" + } + }, + "node_modules/proper-lockfile/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/properties-reader": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/properties-reader/-/properties-reader-2.3.0.tgz", + "integrity": "sha512-z597WicA7nDZxK12kZqHr2TcvwNU1GCfA5UwfDY/HDp3hXPoPlb5rlEx9bwGTiJnc0OqbBTkU975jDToth8Gxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mkdirp": "^1.0.4" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/steveukx/properties?sponsor=1" + } + }, + "node_modules/property-expr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.6.tgz", + "integrity": "sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/property-information": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz", + "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/proto3-json-serializer": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-2.0.2.tgz", + "integrity": "sha512-SAzp/O4Yh02jGdRc+uIrGoe87dkN/XtwxfZ4ZyafJHymd79ozp5VG5nyZ7ygqPM5+cpLDjjGnYFUkngonyDPOQ==", + "license": "Apache-2.0", + "dependencies": { + "protobufjs": "^7.2.5" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/protobufjs": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz", + "integrity": "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/pump": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", + "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/pumpify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-2.0.1.tgz", + "integrity": "sha512-m7KOje7jZxrmutanlkS1daj1dS6z6BgslzOXmcSEpIlCxM3VJH7lG5QLeck/6hgF6F4crFf01UtQmNsJfweTAw==", + "license": "MIT", + "dependencies": { + "duplexify": "^4.1.1", + "inherits": "^2.0.3", + "pump": "^3.0.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/qsu": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/qsu/-/qsu-1.6.4.tgz", + "integrity": "sha512-4PRyFDQhfvz/Np+W2dRJ96bna3Zfby/XOxum9R6hdkA3k8WLHwzcrpJ2lL/zeUYbJJu6ODpKxefJavHk0oCVIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "license": "MIT" + }, + "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==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/quick-format-unescaped": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", + "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==", + "license": "MIT" + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/readdir-glob": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", + "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.1.0" + } + }, + "node_modules/readdir-glob/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/readdirp/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/real-require": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", + "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==", + "license": "MIT", + "engines": { + "node": ">= 12.13.0" + } + }, + "node_modules/rechoir": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", + "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", + "license": "MIT", + "dependencies": { + "resolve": "^1.20.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "license": "MIT" + }, + "node_modules/regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/regex/-/regex-6.0.1.tgz", + "integrity": "sha512-uorlqlzAKjKQZ5P+kTJr3eeJGSVroLKoHmquUj4zHWuR+hEyNqlXsSKlYYF5F4NI6nl7tWCs0apKJ0lmfsXAPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-recursion": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/regex-recursion/-/regex-recursion-6.0.2.tgz", + "integrity": "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-utilities": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/regex-utilities/-/regex-utilities-2.3.0.tgz", + "integrity": "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==", + "dev": true, + "license": "MIT" + }, + "node_modules/regexparam": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/regexparam/-/regexparam-3.0.0.tgz", + "integrity": "sha512-RSYAtP31mvYLkAHrOlh25pCNQ5hWnT106VukGaaFfuJrZFkGRX5GhUAdPqpSDXxOhA2c4akmRuplv1mRqnBn6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/reinterval": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reinterval/-/reinterval-1.1.0.tgz", + "integrity": "sha512-QIRet3SYrGp0HUHO88jVskiG6seqUGC5iAG7AwI/BV4ypGcuqk9Du6YQBUOUqm9c8pw1eyLoIaONifRua1lsEQ==", + "license": "MIT" + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-in-the-middle": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-7.5.1.tgz", + "integrity": "sha512-fgZEz/t3FDrU9o7EhI+iNNq1pNNpJImOvX72HUd6RoFiw8MaKd8/gR5tLuc8A0G0e55LMbP6ImjnmXY6zrTmjw==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.5", + "module-details-from-path": "^1.0.3", + "resolve": "^1.22.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "license": "MIT" + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-dir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", + "integrity": "sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==", + "license": "MIT", + "dependencies": { + "expand-tilde": "^2.0.0", + "global-modules": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-import": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-import/-/resolve-import-2.0.0.tgz", + "integrity": "sha512-jpKjLibLuc8D1XEV2+7zb0aqN7I8d12u89g/v6IsgCzdVlccMQJq4TKkPw5fbhHdxhm7nbVtN+KvOTnjFf+nEA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "glob": "^11.0.0", + "walk-up-path": "^4.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "license": "MIT", + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/restore-cursor/node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ret": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.5.0.tgz", + "integrity": "sha512-I1XxrZSQ+oErkRR4jYbAyEEu2I0avBvvMM5JN+6EBprOGRCs63ENqZ3vjavq8fBw2+62G5LF5XelKwuJpcvcxw==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/retry-request": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-7.0.2.tgz", + "integrity": "sha512-dUOvLMJ0/JJYEn8NrpOaGNE7X3vpI5XlZS/u0ANjqtcZVKnIxP7IgCFwrKTxENw29emmwug53awKtaMm4i9g5w==", + "license": "MIT", + "dependencies": { + "@types/request": "^2.48.8", + "extend": "^3.0.2", + "teeny-request": "^9.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "license": "MIT" + }, + "node_modules/rimraf": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.0.1.tgz", + "integrity": "sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^11.0.0", + "package-json-from-dist": "^1.0.0" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.34.6.tgz", + "integrity": "sha512-wc2cBWqJgkU3Iz5oztRkQbfVkbxoz5EhnCGOrnJvnLnQ7O0WhQUYyv18qQI79O8L7DdHrrlJNeCHd4VGpnaXKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.6" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.34.6", + "@rollup/rollup-android-arm64": "4.34.6", + "@rollup/rollup-darwin-arm64": "4.34.6", + "@rollup/rollup-darwin-x64": "4.34.6", + "@rollup/rollup-freebsd-arm64": "4.34.6", + "@rollup/rollup-freebsd-x64": "4.34.6", + "@rollup/rollup-linux-arm-gnueabihf": "4.34.6", + "@rollup/rollup-linux-arm-musleabihf": "4.34.6", + "@rollup/rollup-linux-arm64-gnu": "4.34.6", + "@rollup/rollup-linux-arm64-musl": "4.34.6", + "@rollup/rollup-linux-loongarch64-gnu": "4.34.6", + "@rollup/rollup-linux-powerpc64le-gnu": "4.34.6", + "@rollup/rollup-linux-riscv64-gnu": "4.34.6", + "@rollup/rollup-linux-s390x-gnu": "4.34.6", + "@rollup/rollup-linux-x64-gnu": "4.34.6", + "@rollup/rollup-linux-x64-musl": "4.34.6", + "@rollup/rollup-win32-arm64-msvc": "4.34.6", + "@rollup/rollup-win32-ia32-msvc": "4.34.6", + "@rollup/rollup-win32-x64-msvc": "4.34.6", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-async": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-3.0.0.tgz", + "integrity": "sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==", + "license": "MIT", + "engines": { + "node": ">=0.12.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==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-regex2": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-4.0.1.tgz", + "integrity": "sha512-goqsB+bSlOmVX+CiFX2PFc1OV88j5jvBqIM+DgqrucHnUguAUNtiNOs+aTadq2NqsLQ+TQ3UEVG3gtSFcdlkCg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "ret": "~0.5.0" + } + }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/sax": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", + "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", + "dev": true, + "license": "ISC" + }, + "node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/schema-utils/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/schema-utils/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "license": "MIT", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/schema-utils/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "license": "MIT" + }, + "node_modules/search-insights": { + "version": "2.17.3", + "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.17.3.tgz", + "integrity": "sha512-RQPdCYTa8A68uM2jwxoY842xDhvx3E5LFL1LxvxCNMev4o5mLuokczhzjAgGwUZBAmOKZknArSxLKmXtIi2AxQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/section-matter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz", + "integrity": "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "extend-shallow": "^2.0.1", + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/secure-json-parse": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-3.0.2.tgz", + "integrity": "sha512-H6nS2o8bWfpFEV6U38sOSjS7bTbdgbCGU9wEM6W14P5H0QOsz94KCusifV44GpHDTu2nqZbuDNhTzu+mjDSw1w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sentence-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/sentence-case/-/sentence-case-3.0.4.tgz", + "integrity": "sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg==", + "license": "MIT", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3", + "upper-case-first": "^2.0.2" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/set-cookie-parser": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", + "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", + "license": "MIT" + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/shiki": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-2.3.2.tgz", + "integrity": "sha512-UZhz/gsUz7DHFbQBOJP7eXqvKyYvMGramxQiSDc83M/7OkWm6OdVHAReEc3vMLh6L6TRhgL9dvhXz9XDkCDaaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/core": "2.3.2", + "@shikijs/engine-javascript": "2.3.2", + "@shikijs/engine-oniguruma": "2.3.2", + "@shikijs/langs": "2.3.2", + "@shikijs/themes": "2.3.2", + "@shikijs/types": "2.3.2", + "@shikijs/vscode-textmate": "^10.0.1", + "@types/hast": "^3.0.4" + } + }, + "node_modules/shiki/node_modules/@shikijs/engine-oniguruma": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-2.3.2.tgz", + "integrity": "sha512-vikMY1TroyZXUHIXbMnvY/mjtOxMn+tavcfAeQPgWS9FHcgFSUoEtywF5B5sOLb9NXb8P2vb7odkh3nj15/00A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "2.3.2", + "@shikijs/vscode-textmate": "^10.0.1" + } + }, + "node_modules/shiki/node_modules/@shikijs/types": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-2.3.2.tgz", + "integrity": "sha512-CBaMY+a3pepyC4SETi7+bSzO0f6hxEQJUUuS4uD7zppzjmrN4ZRtBqxaT+wOan26CR9eeJ5iBhc4qvWEwn7Eeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/vscode-textmate": "^10.0.1", + "@types/hast": "^3.0.4" + } + }, + "node_modules/shimmer": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz", + "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==", + "license": "BSD-2-Clause" + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sinon": { + "version": "19.0.2", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-19.0.2.tgz", + "integrity": "sha512-euuToqM+PjO4UgXeLETsfQiuoyPXlqFezr6YZDFwHR3t4qaX0fZUe1MfPMznTL5f8BWrVS89KduLdMUsxFCO6g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.1", + "@sinonjs/fake-timers": "^13.0.2", + "@sinonjs/samsam": "^8.0.1", + "diff": "^7.0.0", + "nise": "^6.1.1", + "supports-color": "^7.2.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/sinon" + } + }, + "node_modules/sirv": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.0.tgz", + "integrity": "sha512-BPwJGUeDaDCHihkORDchNyyTvWFhcusy1XMmhEVTQTwGeybFbp8YEmB+njbPnth1FibULBSBVwCQni25XlCUDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/snake-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", + "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", + "license": "MIT", + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/sonic-boom": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.0.tgz", + "integrity": "sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==", + "license": "MIT", + "dependencies": { + "atomic-sleep": "^1.0.0" + } + }, + "node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">= 8" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-loader": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-4.0.2.tgz", + "integrity": "sha512-oYwAqCuL0OZhBoSgmdrLa7mv9MjommVMiQIWgcztf+eS4+8BfcUee6nenFnDhKOhzAVnk5gpZdfnz1iiBv+5sg==", + "license": "MIT", + "dependencies": { + "iconv-lite": "^0.6.3", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.72.1" + } + }, + "node_modules/source-map-loader/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/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==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/speakingurl": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz", + "integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/split-ca": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split-ca/-/split-ca-1.0.1.tgz", + "integrity": "sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/ssh-remote-port-forward": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/ssh-remote-port-forward/-/ssh-remote-port-forward-1.0.4.tgz", + "integrity": "sha512-x0LV1eVDwjf1gmG7TTnfqIzf+3VPRz7vrNIjX6oYLbeCrf/PeVY6hkT68Mg+q02qXxQhrLjB0jfgvhevoCRmLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/ssh2": "^0.5.48", + "ssh2": "^1.4.0" + } + }, + "node_modules/ssh-remote-port-forward/node_modules/@types/ssh2": { + "version": "0.5.52", + "resolved": "https://registry.npmjs.org/@types/ssh2/-/ssh2-0.5.52.tgz", + "integrity": "sha512-lbLLlXxdCZOSJMCInKH2+9V/77ET2J6NPQHpFI0kda61Dd1KglJs+fPQBchizmzYSOJBgdTajhPqBO1xxLywvg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/ssh2-streams": "*" + } + }, + "node_modules/ssh2": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.16.0.tgz", + "integrity": "sha512-r1X4KsBGedJqo7h8F5c4Ybpcr5RjyP+aWIG007uBPRjmdQWfEiVLzSK71Zji1B9sKxwaCvD8y8cwSkYrlLiRRg==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "asn1": "^0.2.6", + "bcrypt-pbkdf": "^1.0.2" + }, + "engines": { + "node": ">=10.16.0" + }, + "optionalDependencies": { + "cpu-features": "~0.0.10", + "nan": "^2.20.0" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/std-env": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.8.0.tgz", + "integrity": "sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/stdin-discarder": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", + "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/stoppable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stoppable/-/stoppable-1.1.0.tgz", + "integrity": "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==", + "license": "MIT", + "engines": { + "node": ">=4", + "npm": ">=6" + } + }, + "node_modules/stream-events": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz", + "integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==", + "license": "MIT", + "dependencies": { + "stubs": "^3.0.0" + } + }, + "node_modules/stream-shift": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz", + "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==", + "license": "MIT" + }, + "node_modules/streamx": { + "version": "2.22.0", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.22.0.tgz", + "integrity": "sha512-sLh1evHOzBy/iWRiR6d1zRcLao4gGZr3C1kzNz4fopCOKJb6xD9ub8Mpi9Mr1R6id5o43S+d93fI48UC5uM9aw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-fifo": "^1.3.2", + "text-decoder": "^1.1.0" + }, + "optionalDependencies": { + "bare-events": "^2.2.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "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==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/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==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/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==", + "license": "MIT" + }, + "node_modules/string-width-cjs/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==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "dev": true, + "license": "MIT", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/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==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", + "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strnum": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", + "license": "MIT" + }, + "node_modules/stubs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz", + "integrity": "sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw==", + "license": "MIT" + }, + "node_modules/superjson": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.2.tgz", + "integrity": "sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "copy-anything": "^3.0.2" + }, + "engines": { + "node": ">=16" + } + }, + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/swagger-ui-dist": { + "version": "5.18.3", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.18.3.tgz", + "integrity": "sha512-G33HFW0iFNStfY2x6QXO2JYVMrFruc8AZRX0U/L71aA7WeWfX2E5Nm8E/tsipSZJeIZZbSjUDeynLK/wcuNWIw==", + "license": "Apache-2.0", + "dependencies": { + "@scarf/scarf": "=1.4.0" + } + }, + "node_modules/swc-loader": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/swc-loader/-/swc-loader-0.2.6.tgz", + "integrity": "sha512-9Zi9UP2YmDpgmQVbyOPJClY0dwf58JDyDMQ7uRc4krmc72twNI2fvlBWHLqVekBpPc7h5NJkGVT1zNDxFrqhvg==", + "license": "MIT", + "dependencies": { + "@swc/counter": "^0.1.3" + }, + "peerDependencies": { + "@swc/core": "^1.2.147", + "webpack": ">=2" + } + }, + "node_modules/sync-content": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/sync-content/-/sync-content-2.0.1.tgz", + "integrity": "sha512-NI1mo514yFhr8pV/5Etvgh+pSBUIpoAKoiBIUwALVlQQNAwb40bTw8hhPFaip/dvv0GhpHVOq0vq8iY02ppLTg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "glob": "^11.0.0", + "mkdirp": "^3.0.1", + "path-scurry": "^2.0.0", + "rimraf": "^6.0.0", + "tshy": "^3.0.0" + }, + "bin": { + "sync-content": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sync-content/node_modules/mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/tabbable": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", + "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==", + "dev": true, + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/tar-fs": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.8.tgz", + "integrity": "sha512-ZoROL70jptorGAlgAYiLoBLItEKw/fUxg9BSYK/dF/GAGYFJOJJJMvjPAKDJraCXFwadD456FCuvLWgfhMsPwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0", + "tar-stream": "^3.1.5" + }, + "optionalDependencies": { + "bare-fs": "^4.0.1", + "bare-path": "^3.0.0" + } + }, + "node_modules/tar-stream": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", + "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, + "node_modules/teeny-request": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-9.0.0.tgz", + "integrity": "sha512-resvxdc6Mgb7YEThw6G6bExlXKkv6+YbuzGg9xuXxSgxJF7Ozs+o8Y9+2R3sArdWdW8nOokoQb1yrpFB0pQK2g==", + "license": "Apache-2.0", + "dependencies": { + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "node-fetch": "^2.6.9", + "stream-events": "^1.0.5", + "uuid": "^9.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/teeny-request/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/teeny-request/node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "license": "MIT", + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/teeny-request/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/terser": { + "version": "5.38.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.38.1.tgz", + "integrity": "sha512-GWANVlPM/ZfYzuPHjq0nxT+EbOEDDN3Jwhwdg1D8TU8oSkktp8w64Uq4auuGLxFSoNTRDncTq2hQHX1Ld9KHkA==", + "license": "BSD-2-Clause", + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.3.11", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.11.tgz", + "integrity": "sha512-RVCsMfuD0+cTt3EwX8hSl2Ks56EbFHWmhluwcqoPKtBnfjiT6olaq7PRIRfhyU8nnC2MrnDrBLfrD/RGE+cVXQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "jest-worker": "^27.4.5", + "schema-utils": "^4.3.0", + "serialize-javascript": "^6.0.2", + "terser": "^5.31.1" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/terser-webpack-plugin/node_modules/schema-utils": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz", + "integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==", + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "license": "MIT" + }, + "node_modules/test-exclude": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-7.0.1.tgz", + "integrity": "sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^10.4.1", + "minimatch": "^9.0.4" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/test-exclude/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/test-exclude/node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/test-exclude/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/test-exclude/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/test-exclude/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/testcontainers": { + "version": "10.18.0", + "resolved": "https://registry.npmjs.org/testcontainers/-/testcontainers-10.18.0.tgz", + "integrity": "sha512-MnwWsPjsN5QVe+lSU1LwLZVOyjgwSwv1INzkw8FekdwgvOtvJ7FThQEkbmzRcguQootgwmA9FG54NoTChZDRvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@balena/dockerignore": "^1.0.2", + "@types/dockerode": "^3.3.29", + "archiver": "^7.0.1", + "async-lock": "^1.4.1", + "byline": "^5.0.0", + "debug": "^4.3.5", + "docker-compose": "^0.24.8", + "dockerode": "^3.3.5", + "get-port": "^5.1.1", + "proper-lockfile": "^4.1.2", + "properties-reader": "^2.3.0", + "ssh-remote-port-forward": "^1.0.4", + "tar-fs": "^3.0.6", + "tmp": "^0.2.3", + "undici": "^5.28.5" + } + }, + "node_modules/text-decoder": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz", + "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.6.4" + } + }, + "node_modules/thingies": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/thingies/-/thingies-1.21.0.tgz", + "integrity": "sha512-hsqsJsFMsV+aD4s3CWKk85ep/3I9XzYV/IXaSouJMYIoDlgyi11cBhsqYe9/geRfB0YIikBQg6raRaM+nIMP9g==", + "license": "Unlicense", + "engines": { + "node": ">=10.18" + }, + "peerDependencies": { + "tslib": "^2" + } + }, + "node_modules/thread-stream": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-3.1.0.tgz", + "integrity": "sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==", + "license": "MIT", + "dependencies": { + "real-require": "^0.2.0" + } + }, + "node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "license": "MIT", + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/through2/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/through2/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/through2/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/tiny-case": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-case/-/tiny-case-1.0.3.tgz", + "integrity": "sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.10.tgz", + "integrity": "sha512-Zc+8eJlFMvgatPZTl6A9L/yht8QqdmUNtURHaKZLmKBE12hNPSrqNkUp2cs3M/UKmNVVAMFQYSjYIVHDjW5zew==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.4.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/tinypool": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.2.tgz", + "integrity": "sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + } + }, + "node_modules/tinyrainbow": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", + "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", + "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/title-case": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/title-case/-/title-case-3.0.3.tgz", + "integrity": "sha512-e1zGYRvbffpcHIrnuqT0Dh+gEJtDaxDSoG4JAIpq4oDFyooziLBIiYQv0GBT4FUAnUop5uZ1hiIAj7oAF6sOCA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/tmp": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", + "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.14" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toad-cache": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/toad-cache/-/toad-cache-3.7.0.tgz", + "integrity": "sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/toposort": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz", + "integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==", + "dev": true, + "license": "MIT" + }, + "node_modules/totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, + "node_modules/tree-dump": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tree-dump/-/tree-dump-1.0.2.tgz", + "integrity": "sha512-dpev9ABuLWdEubk+cIaI9cHwRNNDjkBBLXTwI4UCUFdQ5xXKqNXoK4FEciw/vxf+NQ7Cb7sGUyeUtORvHIdRXQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/trouter": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/trouter/-/trouter-4.0.0.tgz", + "integrity": "sha512-bwwr76BThfiVwAFZqks5cJ+VoKNM3/2Yg1ZwJslkdmAUQ6S0UNoCoGYFDxdw+u1skfexggdmD2p35kW5Td4Cug==", + "license": "MIT", + "dependencies": { + "regexparam": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/ts-deepmerge": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/ts-deepmerge/-/ts-deepmerge-7.0.2.tgz", + "integrity": "sha512-akcpDTPuez4xzULo5NwuoKwYRtjQJ9eoNfBACiBMaXwNAx7B1PKfe5wqUFJuW5uKzQ68YjDFwPaWHDG1KnFGsA==", + "license": "ISC", + "engines": { + "node": ">=14.13.1" + } + }, + "node_modules/ts-morph": { + "version": "25.0.1", + "resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-25.0.1.tgz", + "integrity": "sha512-QJEiTdnz1YjrB3JFhd626gX4rKHDLSjSVMvGGG4v7ONc3RBwa0Eei98G9AT9uNFDMtV54JyuXsFeC+OH0n6bXQ==", + "license": "MIT", + "dependencies": { + "@ts-morph/common": "~0.26.0", + "code-block-writer": "^13.0.3" + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "license": "MIT", + "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-node/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, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/tshy": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/tshy/-/tshy-3.0.2.tgz", + "integrity": "sha512-8GkWnAfmNXxl8iDTZ1o2H4jdaj9H7HeDKkr5qd0ZhQBCNA41D3xqTyg2Ycs51VCfmjJ5e+0v9AUmD6ylAI9Bgw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "chalk": "^5.3.0", + "chokidar": "^3.6.0", + "foreground-child": "^3.1.1", + "minimatch": "^10.0.0", + "mkdirp": "^3.0.1", + "polite-json": "^5.0.0", + "resolve-import": "^2.0.0", + "rimraf": "^6.0.0", + "sync-content": "^2.0.1", + "typescript": "^5.5.3", + "walk-up-path": "^4.0.0" + }, + "bin": { + "tshy": "dist/esm/index.js" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/tshy/node_modules/mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/tsx": { + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.2.tgz", + "integrity": "sha512-pOUl6Vo2LUq/bSa8S5q7b91cgNSjctn9ugq/+Mvow99qW6x/UZYwzxy/3NmqoT66eHYfCVvFvACC58UBPFf28g==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.23.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", + "dev": true, + "license": "Unlicense" + }, + "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, + "license": "MIT", + "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==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "license": "MIT" + }, + "node_modules/typedoc": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.27.7.tgz", + "integrity": "sha512-K/JaUPX18+61W3VXek1cWC5gwmuLvYTOXJzBvD9W7jFvbPnefRnCHQCEPw7MSNrP/Hj7JJrhZtDDLKdcYm6ucg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@gerrit0/mini-shiki": "^1.24.0", + "lunr": "^2.3.9", + "markdown-it": "^14.1.0", + "minimatch": "^9.0.5", + "yaml": "^2.6.1" + }, + "bin": { + "typedoc": "bin/typedoc" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "typescript": "5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x" + } + }, + "node_modules/typedoc-plugin-markdown": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-4.4.2.tgz", + "integrity": "sha512-kJVkU2Wd+AXQpyL6DlYXXRrfNrHrEIUgiABWH8Z+2Lz5Sq6an4dQ/hfvP75bbokjNDUskOdFlEEm/0fSVyC7eg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "typedoc": "0.27.x" + } + }, + "node_modules/typedoc/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/typescript": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", + "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "dev": true, + "license": "MIT" + }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "license": "BSD-2-Clause", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/unc-path-regex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", + "integrity": "sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/undici": { + "version": "5.28.5", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.5.tgz", + "integrity": "sha512-zICwjrDrcrUE0pyyJc1I2QzBkLM8FINsgOrt6WjA+BgajVq9Nxu2PbFFXUrAggLfDXlZGZBVZYw7WNV5KiBiBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@fastify/busboy": "^2.0.0" + }, + "engines": { + "node": ">=14.0" + } + }, + "node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "license": "MIT" + }, + "node_modules/unionfs": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/unionfs/-/unionfs-4.5.4.tgz", + "integrity": "sha512-qI3RvJwwdFcWUdZz1dWgAyLSfGlY2fS2pstvwkZBUTnkxjcnIvzriBLtqJTKz9FtArAvJeiVCqHlxhOw8Syfyw==", + "dependencies": { + "fs-monkey": "^1.0.0" + } + }, + "node_modules/unist-util-is": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", + "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", + "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", + "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz", + "integrity": "sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/upper-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-2.0.2.tgz", + "integrity": "sha512-KgdgDGJt2TpuwBUIjgG6lzw2GWFRCW9Qkfkiv0DxqHHLYJHmtmdUIKcZd8rHgFSjopVTlw6ggzCm1b8MFQwikg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/upper-case-first": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-2.0.2.tgz", + "integrity": "sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "license": "MIT", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "node_modules/util": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "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, + "license": "MIT" + }, + "node_modules/v8flags": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-4.0.1.tgz", + "integrity": "sha512-fcRLaS4H/hrZk9hYwbdRM35D0U8IYMfEClhXxCivOojl+yTRAZH3Zy2sSy6qVCiGbV9YAtPssP6jaChqC9vPCg==", + "license": "MIT", + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/vanilla-cookieconsent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vanilla-cookieconsent/-/vanilla-cookieconsent-3.1.0.tgz", + "integrity": "sha512-/McNRtm/3IXzb9dhqMIcbquoU45SzbN2VB+To4jxEPqMmp7uVniP6BhGLjU8MC7ZCDsNQVOp27fhQTM/ruIXAA==", + "license": "MIT" + }, + "node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz", + "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vite": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.1.0.tgz", + "integrity": "sha512-RjjMipCKVoR4hVfPY6GQTgveinjNuyLw+qruksLDvA5ktI1150VmcMBKmQaEWJhg/j6Uaf6dNCNA0AfdzUb/hQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.24.2", + "postcss": "^8.5.1", + "rollup": "^4.30.1" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite-node": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.0.5.tgz", + "integrity": "sha512-02JEJl7SbtwSDJdYS537nU6l+ktdvcREfLksk/NDAqtdKWGqHl+joXzEubHROmS3E6pip+Xgu2tFezMu75jH7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.4.0", + "es-module-lexer": "^1.6.0", + "pathe": "^2.0.2", + "vite": "^5.0.0 || ^6.0.0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vite/node_modules/@esbuild/aix-ppc64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.2.tgz", + "integrity": "sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.2.tgz", + "integrity": "sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.2.tgz", + "integrity": "sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/android-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.2.tgz", + "integrity": "sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.2.tgz", + "integrity": "sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.2.tgz", + "integrity": "sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.2.tgz", + "integrity": "sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.2.tgz", + "integrity": "sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.2.tgz", + "integrity": "sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.2.tgz", + "integrity": "sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ia32": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.2.tgz", + "integrity": "sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-loong64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.2.tgz", + "integrity": "sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-mips64el": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.2.tgz", + "integrity": "sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ppc64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.2.tgz", + "integrity": "sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-riscv64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.2.tgz", + "integrity": "sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-s390x": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.2.tgz", + "integrity": "sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.2.tgz", + "integrity": "sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/netbsd-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.2.tgz", + "integrity": "sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/openbsd-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.2.tgz", + "integrity": "sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/openbsd-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.2.tgz", + "integrity": "sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/sunos-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.2.tgz", + "integrity": "sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.2.tgz", + "integrity": "sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-ia32": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.2.tgz", + "integrity": "sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.2.tgz", + "integrity": "sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/esbuild": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.2.tgz", + "integrity": "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.24.2", + "@esbuild/android-arm": "0.24.2", + "@esbuild/android-arm64": "0.24.2", + "@esbuild/android-x64": "0.24.2", + "@esbuild/darwin-arm64": "0.24.2", + "@esbuild/darwin-x64": "0.24.2", + "@esbuild/freebsd-arm64": "0.24.2", + "@esbuild/freebsd-x64": "0.24.2", + "@esbuild/linux-arm": "0.24.2", + "@esbuild/linux-arm64": "0.24.2", + "@esbuild/linux-ia32": "0.24.2", + "@esbuild/linux-loong64": "0.24.2", + "@esbuild/linux-mips64el": "0.24.2", + "@esbuild/linux-ppc64": "0.24.2", + "@esbuild/linux-riscv64": "0.24.2", + "@esbuild/linux-s390x": "0.24.2", + "@esbuild/linux-x64": "0.24.2", + "@esbuild/netbsd-arm64": "0.24.2", + "@esbuild/netbsd-x64": "0.24.2", + "@esbuild/openbsd-arm64": "0.24.2", + "@esbuild/openbsd-x64": "0.24.2", + "@esbuild/sunos-x64": "0.24.2", + "@esbuild/win32-arm64": "0.24.2", + "@esbuild/win32-ia32": "0.24.2", + "@esbuild/win32-x64": "0.24.2" + } + }, + "node_modules/vitepress": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/vitepress/-/vitepress-1.6.3.tgz", + "integrity": "sha512-fCkfdOk8yRZT8GD9BFqusW3+GggWYZ/rYncOfmgcDtP3ualNHCAg+Robxp2/6xfH1WwPHtGpPwv7mbA3qomtBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@docsearch/css": "3.8.2", + "@docsearch/js": "3.8.2", + "@iconify-json/simple-icons": "^1.2.21", + "@shikijs/core": "^2.1.0", + "@shikijs/transformers": "^2.1.0", + "@shikijs/types": "^2.1.0", + "@types/markdown-it": "^14.1.2", + "@vitejs/plugin-vue": "^5.2.1", + "@vue/devtools-api": "^7.7.0", + "@vue/shared": "^3.5.13", + "@vueuse/core": "^12.4.0", + "@vueuse/integrations": "^12.4.0", + "focus-trap": "^7.6.4", + "mark.js": "8.11.1", + "minisearch": "^7.1.1", + "shiki": "^2.1.0", + "vite": "^5.4.14", + "vue": "^3.5.13" + }, + "bin": { + "vitepress": "bin/vitepress.js" + }, + "peerDependencies": { + "markdown-it-mathjax3": "^4", + "postcss": "^8" + }, + "peerDependenciesMeta": { + "markdown-it-mathjax3": { + "optional": true + }, + "postcss": { + "optional": true + } + } + }, + "node_modules/vitepress-sidebar": { + "version": "1.31.0", + "resolved": "https://registry.npmjs.org/vitepress-sidebar/-/vitepress-sidebar-1.31.0.tgz", + "integrity": "sha512-ctdALc8ptXsD0HuOYMM8xjUu3lSBfHGJJZYtKYuixvjhL+q/AipLeSvsvwfUEhDIwRyi46HQlSa/7XsevVA/Mg==", + "dev": true, + "license": "MIT", + "dependencies": { + "glob": "10.4.5", + "gray-matter": "4.0.3", + "qsu": "^1.6.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/vitepress-sidebar/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/vitepress-sidebar/node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/vitepress-sidebar/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/vitepress-sidebar/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/vitepress-sidebar/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/vitepress/node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitepress/node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitepress/node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitepress/node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitepress/node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitepress/node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitepress/node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitepress/node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitepress/node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitepress/node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitepress/node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitepress/node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitepress/node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitepress/node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitepress/node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitepress/node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitepress/node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitepress/node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitepress/node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitepress/node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitepress/node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitepress/node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitepress/node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitepress/node_modules/@shikijs/types": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-2.3.2.tgz", + "integrity": "sha512-CBaMY+a3pepyC4SETi7+bSzO0f6hxEQJUUuS4uD7zppzjmrN4ZRtBqxaT+wOan26CR9eeJ5iBhc4qvWEwn7Eeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/vscode-textmate": "^10.0.1", + "@types/hast": "^3.0.4" + } + }, + "node_modules/vitepress/node_modules/@vueuse/integrations": { + "version": "12.5.0", + "resolved": "https://registry.npmjs.org/@vueuse/integrations/-/integrations-12.5.0.tgz", + "integrity": "sha512-HYLt8M6mjUfcoUOzyBcX2RjpfapIwHPBmQJtTmXOQW845Y/Osu9VuTJ5kPvnmWJ6IUa05WpblfOwZ+P0G4iZsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vueuse/core": "12.5.0", + "@vueuse/shared": "12.5.0", + "vue": "^3.5.13" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "async-validator": "^4", + "axios": "^1", + "change-case": "^5", + "drauu": "^0.4", + "focus-trap": "^7", + "fuse.js": "^7", + "idb-keyval": "^6", + "jwt-decode": "^4", + "nprogress": "^0.2", + "qrcode": "^1.5", + "sortablejs": "^1", + "universal-cookie": "^7" + }, + "peerDependenciesMeta": { + "async-validator": { + "optional": true + }, + "axios": { + "optional": true + }, + "change-case": { + "optional": true + }, + "drauu": { + "optional": true + }, + "focus-trap": { + "optional": true + }, + "fuse.js": { + "optional": true + }, + "idb-keyval": { + "optional": true + }, + "jwt-decode": { + "optional": true + }, + "nprogress": { + "optional": true + }, + "qrcode": { + "optional": true + }, + "sortablejs": { + "optional": true + }, + "universal-cookie": { + "optional": true + } + } + }, + "node_modules/vitepress/node_modules/change-case": { + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/change-case/-/change-case-5.4.4.tgz", + "integrity": "sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/vitepress/node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/vitepress/node_modules/vite": { + "version": "5.4.14", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.14.tgz", + "integrity": "sha512-EK5cY7Q1D8JNhSaPKVK4pwBFvaTmZxEnoKXLG/U9gmdDcihQGNzFlgIvaxezFR4glP1LsuiedwMBqCXH3wZccA==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vitest": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.0.5.tgz", + "integrity": "sha512-4dof+HvqONw9bvsYxtkfUp2uHsTN9bV2CZIi1pWgoFpL1Lld8LA1ka9q/ONSsoScAKG7NVGf2stJTI7XRkXb2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "3.0.5", + "@vitest/mocker": "3.0.5", + "@vitest/pretty-format": "^3.0.5", + "@vitest/runner": "3.0.5", + "@vitest/snapshot": "3.0.5", + "@vitest/spy": "3.0.5", + "@vitest/utils": "3.0.5", + "chai": "^5.1.2", + "debug": "^4.4.0", + "expect-type": "^1.1.0", + "magic-string": "^0.30.17", + "pathe": "^2.0.2", + "std-env": "^3.8.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.2", + "tinypool": "^1.0.2", + "tinyrainbow": "^2.0.0", + "vite": "^5.0.0 || ^6.0.0", + "vite-node": "3.0.5", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/debug": "^4.1.12", + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "@vitest/browser": "3.0.5", + "@vitest/ui": "3.0.5", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/debug": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/vue": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.13.tgz", + "integrity": "sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.13", + "@vue/compiler-sfc": "3.5.13", + "@vue/runtime-dom": "3.5.13", + "@vue/server-renderer": "3.5.13", + "@vue/shared": "3.5.13" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/walk-up-path": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/walk-up-path/-/walk-up-path-4.0.0.tgz", + "integrity": "sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A==", + "dev": true, + "license": "ISC", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/watchpack": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", + "integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==", + "license": "MIT", + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "license": "MIT", + "dependencies": { + "defaults": "^1.0.3" + } + }, + "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==", + "license": "BSD-2-Clause" + }, + "node_modules/webpack": { + "version": "5.97.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.97.1.tgz", + "integrity": "sha512-EksG6gFY3L1eFMROS/7Wzgrii5mBAFe4rIr3r2BTfo7bcc+DWwFZ4OJ/miOuHJO/A85HwyI4eQ0F6IKXesO7Fg==", + "license": "MIT", + "dependencies": { + "@types/eslint-scope": "^3.7.7", + "@types/estree": "^1.0.6", + "@webassemblyjs/ast": "^1.14.1", + "@webassemblyjs/wasm-edit": "^1.14.1", + "@webassemblyjs/wasm-parser": "^1.14.1", + "acorn": "^8.14.0", + "browserslist": "^4.24.0", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.17.1", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.11", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.2.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.10", + "watchpack": "^2.4.1", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, + "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==", + "license": "MIT", + "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==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.18.tgz", + "integrity": "sha512-qEcY+KJYlWyLH9vNbsr6/5j59AXk5ni5aakf8ldzBvGde6Iz4sxZGkJyWSAueTG7QhOvNRYb1lDdFmL5Td0QKA==", + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "license": "MIT" + }, + "node_modules/worker-timers": { + "version": "7.1.8", + "resolved": "https://registry.npmjs.org/worker-timers/-/worker-timers-7.1.8.tgz", + "integrity": "sha512-R54psRKYVLuzff7c1OTFcq/4Hue5Vlz4bFtNEIarpSiCYhpifHU3aIQI29S84o1j87ePCYqbmEJPqwBTf+3sfw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.24.5", + "tslib": "^2.6.2", + "worker-timers-broker": "^6.1.8", + "worker-timers-worker": "^7.0.71" + } + }, + "node_modules/worker-timers-broker": { + "version": "6.1.8", + "resolved": "https://registry.npmjs.org/worker-timers-broker/-/worker-timers-broker-6.1.8.tgz", + "integrity": "sha512-FUCJu9jlK3A8WqLTKXM9E6kAmI/dR1vAJ8dHYLMisLNB/n3GuaFIjJ7pn16ZcD1zCOf7P6H62lWIEBi+yz/zQQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.24.5", + "fast-unique-numbers": "^8.0.13", + "tslib": "^2.6.2", + "worker-timers-worker": "^7.0.71" + } + }, + "node_modules/worker-timers-worker": { + "version": "7.0.71", + "resolved": "https://registry.npmjs.org/worker-timers-worker/-/worker-timers-worker-7.0.71.tgz", + "integrity": "sha512-ks/5YKwZsto1c2vmljroppOKCivB/ma97g9y77MAAz2TBBjPPgpoOiS1qYQKIgvGTr2QYPT3XhJWIB6Rj2MVPQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.24.5", + "tslib": "^2.6.2" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "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==", + "license": "MIT", + "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-cjs/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==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/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==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/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==", + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/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==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/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==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.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==", + "license": "ISC" + }, + "node_modules/ws": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml-js": { + "version": "1.6.11", + "resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz", + "integrity": "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "sax": "^1.2.4" + }, + "bin": { + "xml-js": "bin/cli.js" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, + "node_modules/yaml": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz", + "integrity": "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "license": "MIT", + "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==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs/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==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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==", + "license": "MIT" + }, + "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==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/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==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.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, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/yoctocolors-cjs": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.2.tgz", + "integrity": "sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yup": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/yup/-/yup-1.6.1.tgz", + "integrity": "sha512-JED8pB50qbA4FOkDol0bYF/p60qSEDQqBD0/qeIrUCG1KbPBIQ776fCUNb9ldbPcSTxA69g/47XTo4TqWiuXOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "property-expr": "^2.0.5", + "tiny-case": "^1.0.3", + "toposort": "^2.0.2", + "type-fest": "^2.19.0" + } + }, + "node_modules/yup/node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zhead": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/zhead/-/zhead-2.2.4.tgz", + "integrity": "sha512-8F0OI5dpWIA5IGG5NHUg9staDwz/ZPxZtvGVf01j7vHqSyZ0raHY+78atOVxRqb73AotX22uV1pXt3gYSstGag==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/harlan-zw" + } + }, + "node_modules/zip-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-6.0.1.tgz", + "integrity": "sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "archiver-utils": "^5.0.0", + "compress-commons": "^6.0.2", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/zod": { + "version": "3.24.1", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.1.tgz", + "integrity": "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-to-json-schema": { + "version": "3.24.1", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.1.tgz", + "integrity": "sha512-3h08nf3Vw3Wl3PK+q3ow/lIil81IT2Oa7YpQyUUDsEWbXveMesdfK1xBd2RhCkynwZndAxixji/7SYJJowr62w==", + "license": "ISC", + "peerDependencies": { + "zod": "^3.24.1" + } + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "packages/amqpbridge": { + "name": "@purista/amqpbridge", + "version": "1.11.0", + "license": "ISC", + "dependencies": { + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/instrumentation-amqplib": "^0.46.0", + "@opentelemetry/resources": "^1.26.0", + "@opentelemetry/sdk-trace-node": "^1.26.0", + "@opentelemetry/semantic-conventions": "^1.27.0", + "@purista/core": "*", + "amqplib": "^0.10.4" + }, + "devDependencies": { + "@types/amqplib": "^0.10.5", + "@types/node": "^22.5.1", + "@types/sinon": "^17.0.3", + "sinon": "^19.0.2", + "tshy": "^3.0.2", + "vitest": "^3.0.4" + }, + "engines": { + "node": ">=18.15" + } + }, + "packages/aws-config-store": { + "name": "@purista/aws-config-store", + "version": "1.11.0", + "license": "ISC", + "dependencies": { + "@aws-sdk/client-ssm": "^3.620.0", + "@purista/core": "*" + }, + "devDependencies": { + "@types/node": "^22.5.1", + "@types/sinon": "^17.0.3", + "sinon": "^19.0.2", + "testcontainers": "^10.12.0", + "tshy": "^3.0.2", + "vitest": "^3.0.4" + }, + "engines": { + "node": ">=18.15" + } + }, + "packages/aws-secret-store": { + "name": "@purista/aws-secret-store", + "version": "1.11.0", + "license": "ISC", + "dependencies": { + "@aws-sdk/client-secrets-manager": "^3.620.0", + "@purista/core": "*" + }, + "devDependencies": { + "@types/node": "^22.5.1", + "@types/sinon": "^17.0.3", + "sinon": "^19.0.2", + "testcontainers": "^10.12.0", + "tshy": "^3.0.2", + "vitest": "^3.0.4" + }, + "engines": { + "node": ">=18.15" + } + }, + "packages/azure-secret-store": { + "name": "@purista/azure-secret-store", + "version": "1.11.0", + "license": "ISC", + "dependencies": { + "@azure/identity": "^4.3.0", + "@azure/keyvault-secrets": "^4.8.0", + "@purista/core": "*" + }, + "devDependencies": { + "@types/node": "^22.5.1", + "@types/sinon": "^17.0.3", + "dotenv": "^16.4.5", + "sinon": "^19.0.2", + "testcontainers": "^10.12.0", + "tshy": "^3.0.2", + "vitest": "^3.0.4" + }, + "engines": { + "node": ">=18.15" + } + }, + "packages/base-http-bridge": { + "name": "@purista/base-http-bridge", + "version": "1.11.0", + "license": "ISC", + "dependencies": { + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/semantic-conventions": "^1.27.0", + "@purista/core": "*", + "cloudevents": "^8.0.2", + "hono": "^4.4.7" + }, + "devDependencies": { + "@types/node": "^22.5.1", + "@types/sinon": "^17.0.3", + "sinon": "^19.0.2", + "tshy": "^3.0.2", + "vitest": "^3.0.4" + }, + "engines": { + "node": ">=18.15" + } + }, + "packages/cli": { + "name": "@purista/cli", + "version": "1.11.0", + "license": "ISC", + "dependencies": { + "camelcase": "^8.0.0", + "minimist": "^1.2.8", + "plop": "^4.0.1", + "ts-morph": "^25.0.0", + "zod": "^3.24.1" + }, + "bin": { + "purista": "dist/index.js" + }, + "devDependencies": { + "@types/minimist": "^1.2.5", + "@types/node": "^22.5.1", + "rimraf": "^6.0.1", + "tshy": "^3.0.2", + "tsx": "^4.15.7" + }, + "engines": { + "node": ">=18.15" + } + }, + "packages/core": { + "name": "@purista/core", + "version": "1.11.0", + "license": "ISC", + "dependencies": { + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/resources": "^1.26.0", + "@opentelemetry/sdk-trace-node": "^1.26.0", + "@opentelemetry/semantic-conventions": "^1.27.0", + "@typeschema/json": "^0.14.0", + "@typeschema/main": "^0.14.0", + "@typeschema/zod": "^0.14.0", + "openapi3-ts": "^4.4.0", + "pino": "^9.2.0", + "ts-deepmerge": "^7.0.0", + "zod": "^3.24.1", + "zod-to-json-schema": "^3.24.1" + }, + "devDependencies": { + "@sodaru/yup-to-json-schema": "^2.0.1", + "@types/node": "^22.5.1", + "@typeschema/yup": "^0.14.0", + "code-block-writer": "^13.0.2", + "rimraf": "^6.0.1", + "sinon": "^19.0.2", + "tshy": "^3.0.2", + "vitest": "^3.0.4", + "yup": "^1.4.0" + }, + "engines": { + "node": ">=18.15" + }, + "peerDependencies": { + "@types/sinon": "^17.0.3", + "code-block-writer": "^13.x", + "rimraf": "^5.x", + "sinon": "^17.x" + }, + "peerDependenciesMeta": { + "@types/sinon": { + "optional": true + }, + "code-block-writer": { + "optional": true + }, + "rimraf": { + "optional": true + }, + "sinon": { + "optional": true + } + } + }, + "packages/dapr-sdk": { + "name": "@purista/dapr-sdk", + "version": "1.11.0", + "license": "ISC", + "dependencies": { + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/semantic-conventions": "^1.27.0", + "@purista/base-http-bridge": "*", + "@purista/core": "*" + }, + "devDependencies": { + "@hono/node-server": "^1.12.2", + "@types/node": "^22.5.1", + "@types/sinon": "^17.0.3", + "hono": "^4.4.7", + "sinon": "^19.0.2", + "tshy": "^3.0.2", + "vitest": "^3.0.4" + }, + "engines": { + "node": ">=18.15" + }, + "peerDependencies": { + "@hono/node-server": "^1.12.0" + }, + "peerDependenciesMeta": { + "@hono/node-server": { + "optional": true + } + } + }, + "packages/gcloud-secret-store": { + "name": "@purista/gcloud-secret-store", + "version": "1.11.0", + "license": "ISC", + "dependencies": { + "@google-cloud/secret-manager": "^5.6.0", + "@purista/core": "*" + }, + "devDependencies": { + "@types/node": "^22.5.1", + "@types/sinon": "^17.0.3", + "sinon": "^19.0.2", + "tshy": "^3.0.2", + "vitest": "^3.0.4" + }, + "engines": { + "node": ">=18.15" + } + }, + "packages/hono-http-server": { + "name": "@purista/hono-http-server", + "version": "1.11.0", + "license": "ISC", + "dependencies": { + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/semantic-conventions": "^1.27.0", + "@purista/core": "*", + "hono": "^4.4.7" + }, + "devDependencies": { + "@hono/swagger-ui": "^0.5.0", + "@types/node": "^22.5.1", + "@types/sinon": "^17.0.3", + "sinon": "^19.0.2", + "tshy": "^3.0.2", + "vitest": "^3.0.4" + }, + "engines": { + "node": ">=18.15" + }, + "peerDependencies": { + "@hono/node-server": "^1.8.0", + "@hono/swagger-ui": "^0.3.0", + "@scalar/hono-api-reference": "^0.5.119" + } + }, + "packages/httpserver": { + "name": "@purista/httpserver", + "version": "1.11.0", + "license": "ISC", + "dependencies": { + "@fastify/compress": "^8.0.1", + "@fastify/cors": "^10.0.1", + "@fastify/helmet": "^13.0.0", + "@fastify/static": "^8.0.3", + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/semantic-conventions": "^1.27.0", + "@purista/core": "*", + "fastify": "^5.1.0", + "openapi3-ts": "4.4.0", + "swagger-ui-dist": "^5.17.14", + "trouter": "^4.0.0" + }, + "devDependencies": { + "@types/node": "^22.5.1", + "@types/qs": "^6.9.15", + "@types/sinon": "^17.0.3", + "@types/swagger-ui-dist": "^3.30.5", + "sinon": "^19.0.2", + "tshy": "^3.0.2", + "vitest": "^3.0.4" + }, + "engines": { + "node": ">=18.15" + } + }, + "packages/infisical-secret-store": { + "name": "@purista/infisical-secret-store", + "version": "1.11.0", + "license": "ISC", + "dependencies": { + "@purista/core": "*" + }, + "devDependencies": { + "@types/node": "^22.5.1", + "@types/sinon": "^17.0.3", + "sinon": "^19.0.2", + "tshy": "^3.0.2", + "vitest": "^3.0.4" + }, + "engines": { + "node": ">=18.15" + } + }, + "packages/k8s-sdk": { + "name": "@purista/k8s-sdk", + "version": "1.11.0", + "license": "ISC", + "dependencies": { + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/semantic-conventions": "^1.27.0", + "@purista/core": "*", + "hono": "^4.4.7" + }, + "devDependencies": { + "@hono/node-server": "^1.12.2", + "@types/node": "^22.5.1", + "@types/sinon": "^17.0.3", + "sinon": "^19.0.2", + "tshy": "^3.0.2", + "vitest": "^3.0.4" + }, + "engines": { + "node": ">=18.15" + }, + "peerDependencies": { + "@hono/node-server": "^1.12.0" + }, + "peerDependenciesMeta": { + "@hono/node-server": { + "optional": true + } + } + }, + "packages/mqttbridge": { + "name": "@purista/mqttbridge", + "version": "1.11.0", + "license": "ISC", + "dependencies": { + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/resources": "^1.26.0", + "@opentelemetry/sdk-trace-node": "^1.26.0", + "@opentelemetry/semantic-conventions": "^1.27.0", + "@purista/core": "*", + "mqtt": "^5.9.0" + }, + "devDependencies": { + "@types/node": "^22.5.1", + "@types/sinon": "^17.0.3", + "@types/ws": "^8.5.12", + "sinon": "^19.0.2", + "tshy": "^3.0.2", + "vitest": "^3.0.4" + }, + "engines": { + "node": ">=18.15" + } + }, + "packages/nats-config-store": { + "name": "@purista/nats-config-store", + "version": "1.11.0", + "license": "ISC", + "dependencies": { + "@purista/core": "*", + "nats": "^2.28.1" + }, + "devDependencies": { + "@testcontainers/nats": "^10.12.0", + "@types/node": "^22.5.1", + "@types/sinon": "^17.0.3", + "sinon": "^19.0.2", + "tshy": "^3.0.2", + "vitest": "^3.0.4" + }, + "engines": { + "node": ">=18.15" + } + }, + "packages/nats-state-store": { + "name": "@purista/nats-state-store", + "version": "1.11.0", + "license": "ISC", + "dependencies": { + "@purista/core": "*", + "nats": "^2.28.1" + }, + "devDependencies": { + "@testcontainers/nats": "^10.12.0", + "@types/node": "^22.5.1", + "@types/sinon": "^17.0.3", + "sinon": "^19.0.2", + "tshy": "^3.0.2", + "vitest": "^3.0.4" + }, + "engines": { + "node": ">=18.15" + } + }, + "packages/natsbridge": { + "name": "@purista/natsbridge", + "version": "1.11.0", + "license": "ISC", + "dependencies": { + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/resources": "^1.26.0", + "@opentelemetry/sdk-trace-node": "^1.26.0", + "@opentelemetry/semantic-conventions": "^1.27.0", + "@purista/core": "*", + "nats": "^2.28.1" + }, + "devDependencies": { + "@testcontainers/nats": "^10.12.0", + "@types/node": "^22.5.1", + "@types/sinon": "^17.0.3", + "@types/ws": "^8.5.12", + "sinon": "^19.0.2", + "tshy": "^3.0.2", + "vitest": "^3.0.4" + }, + "engines": { + "node": ">=18.15" + } + }, + "packages/redis-config-store": { + "name": "@purista/redis-config-store", + "version": "1.11.0", + "license": "ISC", + "dependencies": { + "@purista/core": "*", + "@redis/client": "^1.5.16" + }, + "devDependencies": { + "@types/node": "^22.5.1", + "@types/sinon": "^17.0.3", + "sinon": "^19.0.2", + "tshy": "^3.0.2", + "vitest": "^3.0.4" + }, + "engines": { + "node": ">=18.15" + } + }, + "packages/redis-state-store": { + "name": "@purista/redis-state-store", + "version": "1.11.0", + "license": "ISC", + "dependencies": { + "@purista/core": "*", + "@redis/client": "^1.5.16" + }, + "devDependencies": { + "@types/node": "^22.5.1", + "@types/sinon": "^17.0.3", + "sinon": "^19.0.2", + "tshy": "^3.0.2", + "vitest": "^3.0.4" + }, + "engines": { + "node": ">=18.15" + } + }, + "website": { + "name": "@purista/website", + "version": "1.11.0", + "license": "ISC", + "dependencies": { + "vanilla-cookieconsent": "^3.0.1" + }, + "devDependencies": { + "@types/node": "^22.5.1", + "date-fns": "^4.1.0", + "feed": "^4.2.2", + "gray-matter": "^4.0.3", + "typescript": "^5.5.4", + "vitepress": "^1.2.3", + "vitepress-sidebar": "^1.23.2", + "vue": "^3.4.34" + } + } + } } diff --git a/package.json b/package.json index 7769c6f22..642316799 100644 --- a/package.json +++ b/package.json @@ -1,63 +1,53 @@ { - "version": "1.11.0", - "private": true, - "type": "module", - "scripts": { - "start": "npm run dev -w examples/fullexample", - "build": "npm run build --workspaces --if-present", - "lint": "eslint . --cache .", - "lint:fix": "eslint . --cache . --fix", - "test": "vitest -c vite.config.all.ts", - "test:unit": "vitest -c vite.config.ts", - "test:integration": "vitest -c vite.config.integration.ts", - "update:doc": "npm run gitclean && typedoc && git add --all && git commit -S -am \"doc: update api documentation\" && git-cliff > CHANGELOG.md && git commit -am \"doc: update CHANGELOG\" && npm run build:doc -w website", - "release:major": "npm run gitclean && npm run release:prepare && npm run gitclean && npm version major --no-git-tag-version --workspaces && npm run commit:major && npm publish --workspaces", - "release:minor": "npm run gitclean && npm run release:prepare && npm run gitclean && npm version minor --no-git-tag-version --workspaces && npm run commit:minor && npm publish --workspaces", - "release:patch": "npm run gitclean && npm run release:prepare && npm run gitclean && npm version patch --no-git-tag-version --workspaces && npm run commit:patch && npm publish --workspaces", - "gitclean": "./scripts/isGitClean.sh", - "release:prepare": "npm run lint & npm run build & npm run test", - "tag": "git push && git push --tags && rm -rf build/temp", - "commit:major": "npm version major --no-git-tag-version && scripts/commitVersion.sh && npm run build", - "commit:minor": "npm version minor --no-git-tag-version && scripts/commitVersion.sh && npm run build", - "commit:patch": "npm version patch --no-git-tag-version && scripts/commitVersion.sh && npm run build", - "dev:doc": "npm run dev -w website", - "build:doc": "typedoc && git-cliff > CHANGELOG.md && npm run build:doc -w website", - "vitepress": "npm run preview -w website", - "check:cycling": "npx madge --circular --exclude '\\.d\\.ts$' --extensions ts ./packages", - "sign": "git rebase --exec \"git commit --amend --no-edit -n -S\" -i" - }, - "workspaces": [ - "./packages/*", - "./examples/*", - "website" - ], - "devDependencies": { - "@types/node": "^20.11.17", - "@types/sinon": "^17.0.3", - "@typescript-eslint/eslint-plugin": "^7.0.1", - "@typescript-eslint/parser": "^7.0.1", - "@vitest/coverage-v8": "^1.3.0", - "eslint": "^8.56.0", - "eslint-config-prettier": "^9.1.0", - "eslint-config-standard": "^17.1.0", - "eslint-plugin-import": "^2.29.1", - "eslint-plugin-import-esm": "^2.0.0", - "eslint-plugin-json": "^3.1.0", - "eslint-plugin-node": "^11.1.0", - "eslint-plugin-prettier": "5.1.3", - "eslint-plugin-simple-import-sort": "^12.0.0", - "eslint-plugin-vitest": "^0.3.22", - "git-cliff": "^2.0.4", - "prettier": "^3.2.4", - "rimraf": "^5.0.5", - "sinon": "^17.0.1", - "testcontainers": "^10.6.0", - "ts-node": "^10.9.2", - "tsx": "^4.7.0", - "typedoc": "^0.25.8", - "typedoc-plugin-markdown": "^3.17.1", - "typescript": "^5.3.3", - "vitest": "^1.3.0" - }, - "packageManager": "npm@9.6.2" + "version": "1.11.0", + "private": true, + "type": "module", + "scripts": { + "start": "npm run dev -w examples/fullexample", + "build": "npm run build --workspaces --if-present && npm run lint:fix", + "lint": "npx @biomejs/biome check", + "lint:fix": "npx @biomejs/biome check --write", + "test": "vitest", + "test:unit": "tsc --noEmit && vitest -c ./vitest.config.unit.ts", + "update:doc": "npm run gitclean && typedoc && git add --all && git commit -S -am \"doc: update api documentation\" && git-cliff > CHANGELOG.md && git commit -am \"doc: update CHANGELOG\" && npm run build:doc -w website", + "release:major": "npm run gitclean && npm run release:prepare && npm run gitclean && npm version major --no-git-tag-version --workspaces && npm run commit:major && npm publish --workspaces", + "release:minor": "npm run gitclean && npm run release:prepare && npm run gitclean && npm version minor --no-git-tag-version --workspaces && npm run commit:minor && npm publish --workspaces", + "release:patch": "npm run gitclean && npm run release:prepare && npm run gitclean && npm version patch --no-git-tag-version --workspaces && npm run commit:patch && npm publish --workspaces", + "gitclean": "./scripts/isGitClean.sh", + "release:prepare": "npm run lint & npm run build & npm run test", + "tag": "git push && git push --tags && rm -rf build/temp", + "commit:major": "npm version major --no-git-tag-version && scripts/commitVersion.sh && npm run build", + "commit:minor": "npm version minor --no-git-tag-version && scripts/commitVersion.sh && npm run build", + "commit:patch": "npm version patch --no-git-tag-version && scripts/commitVersion.sh && npm run build", + "dev:doc": "npm run dev -w website", + "build:doc": "typedoc && git-cliff > CHANGELOG.md && npm run build:doc -w website", + "vitepress": "npm run preview -w website", + "check:cycling": "npx madge --circular --exclude '\\.d\\.ts$' --extensions ts ./packages", + "sign": "git rebase --exec \"git commit --amend --no-edit -n -S\" -i", + "deps:check": "ncu -ws --root", + "deps:update": "ncu -ws --root -u" + }, + "workspaces": ["./packages/*", "./examples/*", "website"], + "devDependencies": { + "@biomejs/biome": "^1.9.4", + "@types/node": "^22.5.1", + "@types/sinon": "^17.0.3", + "@vitest/coverage-v8": "^3.0.4", + "@vitest/ui": "^3.0.4", + "git-cliff": "^2.3.0", + "npm-check-updates": "^17.1.0", + "rimraf": "^6.0.1", + "sinon": "^19.0.2", + "testcontainers": "^10.12.0", + "ts-node": "^10.9.2", + "tsx": "^4.19.0", + "typedoc": "^0.27.3", + "typedoc-plugin-markdown": "^4.3.1", + "typescript": "^5.5.4", + "vitest": "^3.0.4" + }, + "packageManager": "npm@9.6.2", + "dependencies": { + "npm": "^11.1.0" + } } diff --git a/packages/amqpbridge/jsr.json b/packages/amqpbridge/jsr.json new file mode 100644 index 000000000..4a5e94979 --- /dev/null +++ b/packages/amqpbridge/jsr.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://jsr.io/schema/config-file.v1.json", + "name": "@purista/amqpbridge", + "version": "1.11.0", + "description": "AMQP eventbridge for PURISTA backend framework", + "keywords": ["purista", "amqp", "typescript", "javascript"], + "exports": "./dist/esm/index.js", + "publish": { + "include": ["dist/**/*.js", "dist/**/*.d.ts", "README.md", "package.json"], + "exclude": [ + "src", + ".github", + ".vscode", + ".zed", + "!dist", + "!dist/**/*.js", + "!dist/**/*.d.ts", + ".tshy", + ".tshy-build", + "vendor", + "docs", + "typedoc.json", + "..eslintcache", + ".npmignore" + ] + } +} diff --git a/packages/amqpbridge/package.json b/packages/amqpbridge/package.json index cfe1f3a1c..f29ff0c88 100644 --- a/packages/amqpbridge/package.json +++ b/packages/amqpbridge/package.json @@ -1,68 +1,66 @@ { - "name": "@purista/amqpbridge", - "version": "1.11.0", - "description": "AMQP eventbridge for PURISTA backend framework", - "homepage": "https://purista.dev", - "repository": { - "type": "git", - "url": "git@github.com:sebastianwessel/purista.git" - }, - "author": "Sebastian Wessel", - "license": "ISC", - "type": "module", - "main": "./dist/commonjs/index.js", - "exports": { - "./package.json": "./package.json", - ".": { - "import": { - "types": "./dist/esm/index.d.ts", - "default": "./dist/esm/index.js" - }, - "require": { - "types": "./dist/commonjs/index.d.ts", - "default": "./dist/commonjs/index.js" - } - } - }, - "files": [ - "dist/**/*" - ], - "publishConfig": { - "access": "public" - }, - "engines": { - "node": ">=18.15" - }, - "scripts": { - "lint": "eslint . --ext .ts,.json --cache . --fix", - "test": "vitest", - "build": "rimraf dist && tshy" - }, - "tshy": { - "exclude": [ - "src/**/*.test.ts" - ], - "exports": { - "./package.json": "./package.json", - ".": "./src/index.ts" - } - }, - "devDependencies": { - "@types/amqplib": "^0.10.4", - "@types/node": "^20.11.17", - "@types/sinon": "^17.0.3", - "sinon": "^17.0.1", - "tshy": "^1.11.1", - "vitest": "^1.3.0" - }, - "dependencies": { - "@opentelemetry/api": "^1.7.0", - "@opentelemetry/resources": "^1.19.0", - "@opentelemetry/sdk-trace-node": "^1.19.0", - "@opentelemetry/semantic-conventions": "^1.19.0", - "@purista/core": "*", - "amqplib": "^0.10.3" - }, - "peerDependenciesMeta": {}, - "types": "./dist/commonjs/index.d.ts" + "name": "@purista/amqpbridge", + "version": "1.11.0", + "description": "AMQP eventbridge for PURISTA backend framework", + "homepage": "https://purista.dev", + "repository": { + "type": "git", + "url": "git@github.com:puristajs/purista.git" + }, + "author": "Sebastian Wessel", + "license": "ISC", + "type": "module", + "main": "./dist/commonjs/index.js", + "exports": { + "./package.json": "./package.json", + ".": { + "import": { + "types": "./dist/esm/index.d.ts", + "default": "./dist/esm/index.js" + }, + "require": { + "types": "./dist/commonjs/index.d.ts", + "default": "./dist/commonjs/index.js" + } + } + }, + "files": ["dist/**/*"], + "publishConfig": { + "access": "public" + }, + "engines": { + "node": ">=18.15" + }, + "scripts": { + "lint": "npx @biomejs/biome check --write", + "test": "vitest", + "build": "rimraf dist && tshy" + }, + "tshy": { + "exclude": ["src/**/*.test.ts"], + "exports": { + "./package.json": "./package.json", + ".": "./src/index.ts" + } + }, + "devDependencies": { + "@types/amqplib": "^0.10.5", + "@types/node": "^22.5.1", + "@types/sinon": "^17.0.3", + "sinon": "^19.0.2", + "tshy": "^3.0.2", + "vitest": "^3.0.4" + }, + "dependencies": { + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/resources": "^1.26.0", + "@opentelemetry/sdk-trace-node": "^1.26.0", + "@opentelemetry/semantic-conventions": "^1.27.0", + "@opentelemetry/instrumentation-amqplib": "^0.46.0", + "@purista/core": "*", + "amqplib": "^0.10.4" + }, + "peerDependenciesMeta": {}, + "types": "./dist/commonjs/index.d.ts", + "module": "./dist/esm/index.js" } diff --git a/packages/amqpbridge/src/AmqpBridge.impl.ts b/packages/amqpbridge/src/AmqpBridge.impl.ts index 7159a4c54..b32d3f7bf 100644 --- a/packages/amqpbridge/src/AmqpBridge.impl.ts +++ b/packages/amqpbridge/src/AmqpBridge.impl.ts @@ -1,38 +1,38 @@ import { SpanKind, SpanStatusCode, trace } from '@opentelemetry/api' import type { - Command, - CommandDefinitionMetadataBase, - CommandErrorResponse, - CommandSuccessResponse, - CustomMessage, - DefinitionEventBridgeConfig, - EBMessage, - EBMessageAddress, - EBMessageId, - EventBridge, - EventBridgeConfig, - PendigInvocation, - Subscription, + Command, + CommandDefinitionMetadataBase, + CommandErrorResponse, + CommandSuccessResponse, + CustomMessage, + DefinitionEventBridgeConfig, + EBMessage, + EBMessageAddress, + EBMessageId, + EventBridge, + EventBridgeConfig, + PendigInvocation, + Subscription, } from '@purista/core' import { - createInfoMessage, - deserializeOtp, - EBMessageType, - EventBridgeBaseClass, - EventBridgeEventNames, - getCleanedMessage, - getNewCorrelationId, - getNewEBMessageId, - HandledError, - isCommandErrorResponse, - isCommandResponse, - isCommandSuccessResponse, - isInfoMessage, - PuristaSpanName, - PuristaSpanTag, - serializeOtp, - StatusCode, - UnhandledError, + EBMessageType, + EventBridgeBaseClass, + EventBridgeEventNames, + HandledError, + PuristaSpanName, + PuristaSpanTag, + StatusCode, + UnhandledError, + createInfoMessage, + deserializeOtp, + getCleanedMessage, + getNewCorrelationId, + getNewEBMessageId, + isCommandErrorResponse, + isCommandResponse, + isCommandSuccessResponse, + isInfoMessage, + serializeOtp, } from '@purista/core' import type { Channel, Connection } from 'amqplib' import amqplib from 'amqplib' @@ -65,809 +65,809 @@ import type { AmqpBridgeConfig, Encoder, Encrypter } from './types/index.js' * @group Event bridge */ export class AmqpBridge extends EventBridgeBaseClass implements EventBridge { - protected connection?: Connection - protected channel?: Channel - - protected healthy = false - protected ready = false - - protected consumerTags: string[] = [] - - protected replyQueueName?: string - protected serviceFunctions = new Map< - string, - { - cb: (message: Command) => Promise - channel: Channel - } - >() - - protected pendingInvocations = new Map() - - protected runningSubscriptionCount = 0 - - protected subscriptions = new Map< - string, - { - cb: (message: CustomMessage) => Promise | undefined> - channel: Channel - } - >() - - protected encoder: Encoder = { - ...jsonEncoder, - } - - protected encrypter: Encrypter = { - ...plainEncrypter, - } - - constructor(config?: EventBridgeConfig) { - //= getDefaultConfig() - const conf = { - ...getDefaultConfig(), - ...config, - } - super('AmqpBridge', conf) - - this.encoder = { - ...this.encoder, - ...this.config.encoder, - } - - this.encrypter = { - ...this.encrypter, - ...this.config.encrypter, - } - } - - async isReady() { - return this.ready - } - - async isHealthy() { - return this.healthy - } - - /** - * Connect to RabbitMQ broker, ensure exchange, call back queue - */ - async start() { - await super.start() - try { - this.connection = await amqplib.connect(this.config.url ?? getDefaultConfig().url, this.config.socketOptions) - } catch (err) { - this.emit(EventBridgeEventNames.EventbridgeConnectionError, err) - this.logger.fatal({ err }, 'unable to connect to broker') - throw err - } - - this.connection.on('error', (err) => { - this.healthy = false - this.logger.error({ err }, 'amqp lib error') - this.emit(EventBridgeEventNames.EventbridgeError, err) - }) - this.connection.on('close', () => { - this.healthy = false - this.ready = false - this.emit(EventBridgeEventNames.EventbridgeDisconnected) - this.logger.info('amqp connection disconnected') - }) - - this.emit(EventBridgeEventNames.EventbridgeConnected) - this.logger.info('connected to broker') - this.channel = await this.connection.createChannel() - - this.channel.on('close', () => { - this.healthy = false - this.ready = false - this.logger.info('channel closed') - this.emit(EventBridgeEventNames.EventbridgeDisconnected) - }) - - this.channel.on('error', (err) => { - this.healthy = false - this.logger.error({ err }, 'amqp channel error') - this.emit(EventBridgeEventNames.EventbridgeError, err) - }) - - this.logger.debug('ensured: default exchange') - await this.channel.assertExchange( - this.config.exchangeName ?? getDefaultConfig().exchangeName, - 'headers', - this.config.exchangeOptions, - ) - const responseQueue = await this.channel.assertQueue('', { exclusive: true, autoDelete: true, durable: false }) - this.replyQueueName = responseQueue.queue - await this.channel.bindQueue(this.replyQueueName, this.config.exchangeName ?? getDefaultConfig().exchangeName, '', { - 'x-match': 'all', - replyTo: this.replyQueueName, - }) - const consume = await this.channel.consume( - this.replyQueueName, - async (msg) => { - if (!msg) { - return - } - const context = await deserializeOtpFromAmqpHeader(this.logger, msg, this.encrypter, this.encoder) - return this.startActiveSpan( - PuristaSpanName.EventBridgeCommandResponseReceived, - { kind: SpanKind.CONSUMER }, - context, - async (span) => { - try { - const message = await this.decodeContent( - msg.content, - msg.properties.contentType, - msg.properties.contentEncoding, - ) - - if (message.eventName) { - span.addEvent(message.eventName) - } - - const log = this.logger.getChildLogger({ customTraceId: message.traceId, ...span.spanContext() }) - - if (isCommandResponse(message)) { - const mapEntry = this.pendingInvocations.get(message.correlationId) - if (!mapEntry) { - const err = new UnhandledError( - StatusCode.BadRequest, - 'InvalidCommandResponse: received invalid command response', - getCleanedMessage(message), - ) - span.setStatus({ - code: SpanStatusCode.ERROR, - message: err.message, - }) - span.recordException(err) - log.error({ err }, 'received invalid command response') - this.emit(EventBridgeEventNames.EventbridgeError, err) - return - } - if (isCommandSuccessResponse(message)) { - mapEntry.resolve(message.payload) - } else if (isCommandErrorResponse(message)) { - const error = message.isHandledError - ? HandledError.fromMessage(message) - : UnhandledError.fromMessage(message) - span.recordException(error) - log.error({ err: error }, error.message) - mapEntry.reject(error) - } - return - } - - if (isInfoMessage(message)) { - log.trace('info message', message) - return - } - - const err = new UnhandledError(StatusCode.BadRequest, 'InvalidMessage: received invalid message', message) - span.setStatus({ - code: SpanStatusCode.ERROR, - message: err.message, - }) - span.recordException(err) - log.error({ err }, 'received invalid message') - this.emit(EventBridgeEventNames.EventbridgeError, err) - } catch (error) { - const err = new HandledError(StatusCode.InternalServerError, 'failed to handle response message', error) - span.setStatus({ - code: SpanStatusCode.ERROR, - message: err.message, - }) - span.recordException(err) - this.emit(EventBridgeEventNames.EventbridgeError, err) - this.logger.error({ err, ...span.spanContext() }, 'failed to handle response message') - } - }, - ) - }, - { noAck: true }, - ) - - this.consumerTags.push(consume.consumerTag) - - this.healthy = true - this.ready = true - this.logger.debug('ensured: response queue') - - this.logger.info('amqp event bridge ready') - } - - async emitMessage( - message: Omit, - contentType = 'application/json', - contentEncoding = 'utf-8', - ): Promise> { - const context = deserializeOtp(this.logger, message.otp) - - const name = isCommandResponse(message as EBMessage) - ? PuristaSpanName.EventBridgeCommandResponseSent - : PuristaSpanName.EventBridgeEmitMessage - - return this.startActiveSpan(name, { kind: SpanKind.PRODUCER }, context, async (span) => { - if (!this.channel) { - const err = new UnhandledError( - StatusCode.InternalServerError, - 'emit message: failed No channel - not connected', - ) - span.setStatus({ - code: SpanStatusCode.ERROR, - message: err.message, - }) - span.recordException(err) - this.logger.error({ err, ...span.spanContext() }, err.message) - throw err - } - - const msg = Object.freeze({ - ...message, - id: getNewEBMessageId(), - timestamp: Date.now(), - traceId: message.traceId, - otp: serializeOtp(), - sender: { - ...message.sender, - instanceId: this.instanceId, - }, - }) - - span.setAttribute(PuristaSpanTag.SenderServiceName, msg.sender.serviceName) - span.setAttribute(PuristaSpanTag.SenderServiceVersion, msg.sender.serviceVersion) - span.setAttribute(PuristaSpanTag.SenderServiceTarget, msg.sender.serviceTarget) - - if (msg.eventName) { - span.addEvent(msg.eventName) - } - - const headers: Record = { - messageType: msg.messageType, - senderServiceName: msg.sender.serviceName, - senderServiceVersion: msg.sender.serviceVersion, - senderServiceTarget: msg.sender.serviceTarget, - senderInstanceId: msg.sender.instanceId, - eventName: msg.eventName, - principalId: msg.principalId, - tenantId: msg.tenantId, - } - - serializeOtpForAmqpHeader(headers) - - const payload = await this.encodeContent(msg, contentType, contentEncoding) - - await this.channel.publish(this.config.exchangeName ?? getDefaultConfig().exchangeName, '', payload, { - messageId: msg.id, - timestamp: msg.timestamp, - contentType, - contentEncoding, - type: msg.messageType, - headers, - persistent: true, - }) - - return msg as Readonly - }) - } - - async invoke( - input: Omit, - commandTimeout: number = this.defaultCommandTimeout, - ): Promise { - const context = deserializeOtp(this.logger, input.otp) - return this.startActiveSpan( - PuristaSpanName.EventBridgeInvokeCommand, - { kind: SpanKind.PRODUCER }, - context, - async (span) => { - if (!this.channel) { - const err = new UnhandledError(StatusCode.InternalServerError, 'invoke failed: No channel - not connected') - span.setStatus({ - code: SpanStatusCode.ERROR, - message: err.message, - }) - span.recordException(err) - this.logger.error({ err, ...span.spanContext() }, err.message) - throw err - } - - const correlationId = getNewCorrelationId() - - const command: Command = Object.freeze({ - ...input, - id: getNewEBMessageId(), - correlationId, - timestamp: Date.now(), - messageType: EBMessageType.Command, - traceId: input.traceId, - otp: serializeOtp(), - sender: { - ...input.sender, - instanceId: this.instanceId, - }, - }) - - const removeFromPending = () => { - this.pendingInvocations.delete(correlationId) - } - - const executionPromise = new Promise((resolve, reject) => { - const timeout = setTimeout(() => { - const err = new UnhandledError( - StatusCode.GatewayTimeout, - 'invocation timed out', - undefined, - command.traceId, - ) - this.logger.warn({ err }) - rejectFn(err) - }, commandTimeout) - - const resolveFn = (successPayload: T) => { - clearTimeout(timeout) - removeFromPending() - resolve(successPayload) - } - - const rejectFn = (err: unknown) => { - clearTimeout(timeout) - removeFromPending() - reject(err) - } - - this.pendingInvocations.set(command.correlationId, { - resolve: resolveFn, - reject: rejectFn, - }) - }) - - span.setAttribute(PuristaSpanTag.SenderServiceName, command.sender.serviceName) - span.setAttribute(PuristaSpanTag.SenderServiceVersion, command.sender.serviceVersion) - span.setAttribute(PuristaSpanTag.SenderServiceTarget, command.sender.serviceTarget) - span.setAttribute(PuristaSpanTag.ReceiverServiceName, command.receiver.serviceName) - span.setAttribute(PuristaSpanTag.ReceiverServiceVersion, command.receiver.serviceVersion) - span.setAttribute(PuristaSpanTag.ReceiverServiceTarget, command.receiver.serviceTarget) - - const headers: Record = { - messageType: command.messageType, - senderServiceName: command.sender.serviceName, - senderServiceVersion: command.sender.serviceVersion, - senderServiceTarget: command.sender.serviceTarget, - senderInstanceId: command.sender.instanceId, - receiverServiceName: command.receiver.serviceName, - receiverServiceVersion: command.receiver.serviceVersion, - receiverServiceTarget: command.receiver.serviceTarget, - eventName: command.eventName, - principalId: command.principalId, - tenantId: command.tenantId, - } - serializeOtpForAmqpHeader(headers) - - const content = await this.encodeContent(command, 'application/json', 'utf-8') - - this.channel.publish(this.config.exchangeName ?? getDefaultConfig().exchangeName, '', content, { - messageId: command.id, - timestamp: command.timestamp, - correlationId: command.correlationId, - contentType: 'application/json', - contentEncoding: 'utf-8', - type: command.messageType, - headers, - replyTo: this.replyQueueName, - persistent: true, - }) - - return executionPromise - }, - ) - } - - /** - * Register a service function and ensure that there is a queue for all incoming command requests. - * @param address The service function address - * @param cb the function to call if a matching command message arrives - * @returns the id of command function queue - */ - async registerCommand( - address: EBMessageAddress, - cb: (message: Command) => Promise, - metadata: CommandDefinitionMetadataBase, - eventBridgeConfig: DefinitionEventBridgeConfig, - ): Promise { - if (!this.connection) { - throw new Error('No connection - not connected') - } - - const queueName = getCommandQueueName(address, this.config.namePrefix) - - const channel = await this.connection.createChannel() - - const noAck = eventBridgeConfig.autoacknowledge ?? true - - channel.on('close', () => { - this.healthy = false - this.logger.info({ queueName }, 'channel for command closed') - this.emit(EventBridgeEventNames.EventbridgeDisconnected) - }) - - channel.on('error', (err) => { - this.healthy = false - this.logger.error({ err, queueName }, 'command channel error') - this.emit(EventBridgeEventNames.EventbridgeError, err) - }) - - const queue = await channel.assertQueue(queueName, { durable: !!eventBridgeConfig.durable, autoDelete: true }) - await channel.bindQueue(queue.queue, this.config.exchangeName ?? getDefaultConfig().exchangeName, '', { - 'x-match': 'all', - messageType: EBMessageType.Command, - receiverServiceName: address.serviceName, - receiverServiceVersion: address.serviceVersion, - receiverServiceTarget: address.serviceTarget, - }) - - const consume = await channel.consume( - queue.queue, - async (msg) => { - const context = await deserializeOtpFromAmqpHeader(this.logger, msg, this.encrypter, this.encoder) - return this.startActiveSpan( - PuristaSpanName.EventBridgeCommandReceived, - { kind: SpanKind.CONSUMER }, - context, - async (span) => { - if (!msg) { - return - } - try { - const command = await this.decodeContent( - msg.content, - msg.properties.contentType, - msg.properties.contentEncoding, - ) - - command.otp = serializeOtp() - - const result = await cb(command) - - const returnContext = deserializeOtp(this.logger, result.otp) - return this.startActiveSpan( - PuristaSpanName.EventBridgeCommandResponseSent, - { kind: SpanKind.PRODUCER }, - returnContext, - async (subSpan) => { - const responseMessage = { - ...result, - otp: serializeOtp(), - sennder: { - ...result.sender, - instanceId: this.instanceId, - }, - } - - subSpan.setAttribute(PuristaSpanTag.SenderServiceName, responseMessage.sender.serviceName) - subSpan.setAttribute(PuristaSpanTag.SenderServiceVersion, responseMessage.sender.serviceVersion) - subSpan.setAttribute(PuristaSpanTag.SenderServiceTarget, responseMessage.sender.serviceTarget) - - if (responseMessage.eventName) { - subSpan.addEvent(responseMessage.eventName) - } - - const headers: Record = { - messageType: responseMessage.messageType, - senderServiceName: responseMessage.sender.serviceName, - senderServiceVersion: responseMessage.sender.serviceVersion, - senderServiceTarget: responseMessage.sender.serviceTarget, - senderInstanceId: responseMessage.sender.instanceId, - receiverServiceName: responseMessage.receiver.serviceName, - receiverServiceVersion: responseMessage.receiver.serviceVersion, - receiverServiceTarget: responseMessage.receiver.serviceTarget, - receiverServiceInstanceId: responseMessage.receiver.instanceId, - replyTo: msg.properties.replyTo, - eventName: responseMessage.eventName, - principalId: responseMessage.principalId, - tenantId: responseMessage.tenantId, - } - - serializeOtpForAmqpHeader(headers) - - const contentType = 'application/json' - const contentEncoding = 'utf-8' - - const payload = await this.encodeContent(responseMessage, contentType, contentEncoding) - - this.channel?.publish(this.config.exchangeName ?? getDefaultConfig().exchangeName, '', payload, { - messageId: responseMessage.id, - timestamp: responseMessage.timestamp, - correlationId: msg.properties.correlationId, - contentType, - contentEncoding, - type: responseMessage.messageType, - headers, - persistent: true, - }) - - if (!noAck) { - channel.ack(msg) - } - }, - ) - } catch (error) { - const err = new UnhandledError( - StatusCode.InternalServerError, - 'Failed to consume command response message', - { - error, - }, - ) - span.setStatus({ - code: SpanStatusCode.ERROR, - message: err.message, - }) - span.recordException(err) - this.emit(EventBridgeEventNames.EventbridgeError, err) - this.logger.error({ err }, 'Failed to consume command response message') - if (!noAck) { - channel.nack(msg) - } - } - }, - ) - }, - { noAck }, - ) - - this.consumerTags.push(consume.consumerTag) - - this.serviceFunctions.set(queueName, { cb, channel }) - - const info = createInfoMessage( - EBMessageType.InfoServiceFunctionAdded, - { ...address, instanceId: this.instanceId }, - { - payload: metadata, - }, - ) - await this.emitMessage(info) - - return queueName - } - - async unregisterCommand(address: EBMessageAddress): Promise { - try { - const queueName = getCommandQueueName(address) - const entry = this.serviceFunctions.get(queueName) - if (!entry) { - return - } - await entry.channel.close() - this.serviceFunctions.delete(queueName) - } catch (error) { - const err = new UnhandledError(StatusCode.InternalServerError, 'Failed to unregister service function', { - error, - address, - }) - this.emit(EventBridgeEventNames.EventbridgeError, err) - this.logger.error({ err }, 'Failed to unregister service function') - } - } - - async registerSubscription( - subscription: Subscription, - cb: (message: EBMessage) => Promise | undefined>, - ): Promise { - if (!this.connection) { - throw new Error('No connection - not connected') - } - - const noAck = !!subscription.eventBridgeConfig.autoacknowledge - - const isShared = subscription.eventBridgeConfig.shared === undefined || subscription.eventBridgeConfig.shared - - const queueName = isShared ? getSubscriptionQueueName(subscription.subscriber, this.config.namePrefix) : '' - - const queueOptions: amqplib.Options.AssertQueue = isShared - ? { - durable: subscription.eventBridgeConfig.durable, - } - : { exclusive: true, autoDelete: true, durable: false } - - const channel = await this.connection.createChannel() - - channel.on('close', () => { - this.healthy = false - this.logger.info({ queueName }, 'channel for subscription closed') - this.emit(EventBridgeEventNames.EventbridgeDisconnected) - }) - - channel.on('error', (err) => { - this.healthy = false - this.logger.error({ err, queueName }, 'subscription channel error') - this.emit(EventBridgeEventNames.EventbridgeError, err) - }) - - const queue = await channel.assertQueue(queueName, queueOptions) - await channel.bindQueue(queue.queue, this.config.exchangeName ?? getDefaultConfig().exchangeName, '', { - 'x-match': 'all', - messageType: subscription.messageType, - senderServiceName: subscription.sender?.serviceName, - senderServiceVersion: subscription.sender?.serviceVersion, - senderServiceTarget: subscription.sender?.serviceTarget, - senderInstanceId: subscription.sender?.instanceId, - receiverServiceName: subscription.receiver?.serviceName, - receiverServiceVersion: subscription.receiver?.serviceVersion, - receiverServiceTarget: subscription.receiver?.serviceTarget, - receiverInstanceId: subscription.receiver?.instanceId, - eventName: subscription.eventName, - principalId: subscription.principalId, - tenantId: subscription.tenantId, - }) - - const consume = await channel.consume( - queue.queue, - async (msg) => { - const context = await deserializeOtpFromAmqpHeader(this.logger, msg, this.encrypter, this.encoder) - - const spanContext = context ? trace.getSpanContext(context) : undefined - this.startActiveSpan( - PuristaSpanName.EventBridgeSubscriptionEventReceived, - { kind: SpanKind.CONSUMER, links: spanContext ? [{ context: spanContext }] : [] }, - context, - async (span) => { - if (!msg) { - return - } - this.runningSubscriptionCount++ - try { - const message = await this.decodeContent( - msg.content, - msg.properties.contentType, - msg.properties.contentEncoding, - ) - - span.setAttribute(PuristaSpanTag.SenderServiceName, message.sender.serviceName) - span.setAttribute(PuristaSpanTag.SenderServiceVersion, message.sender.serviceVersion) - span.setAttribute(PuristaSpanTag.SenderServiceTarget, message.sender.serviceTarget) - - if (message.eventName) { - span.addEvent(message.eventName) - } - - message.otp = serializeOtp() - - const result = await cb(message) - if (subscription.emitEventName && result) { - await this.emitMessage(result) - } - if (!noAck) { - channel.ack(msg) - } - this.runningSubscriptionCount-- - } catch (error) { - this.runningSubscriptionCount-- - const err = new UnhandledError(StatusCode.InternalServerError, 'Failed to consume subscription message', { - error, - subscription, - }) - span.setStatus({ - code: SpanStatusCode.ERROR, - message: err.message, - }) - span.recordException(err) - this.emit(EventBridgeEventNames.EventbridgeError, err) - this.logger.error({ err }, 'Failed to consume subscription message') - if (!noAck) { - channel.nack(msg) - } - } - }, - ) - }, - { noAck }, - ) - - this.consumerTags.push(consume.consumerTag) - - this.subscriptions.set(queueName, { cb, channel }) - return queueName - } - - async unregisterSubscription(address: EBMessageAddress): Promise { - try { - const queueName = getSubscriptionQueueName(address) - const entry = this.subscriptions.get(queueName) - if (!entry) { - return - } - await entry.channel.close() - this.subscriptions.delete(queueName) - } catch (error) { - const err = new UnhandledError(StatusCode.InternalServerError, 'Failed to unregister subscription', { - error, - address, - }) - this.emit(EventBridgeEventNames.EventbridgeError, err) - this.logger.error({ err }, 'Failed to unregister subscription') - } - } - - /** - * Encode given payload to buffer - * @param input - * @param contentType - * @param contentEncoding - * @returns - */ - protected async encodeContent(input: T, contentType: string, contentEncoding: string): Promise { - const encoder = this.encoder[contentType] - if (!encoder) { - throw new Error(`Encode not defined for ${contentType}`) - } - const encodedPayload = await encoder.encode(input) - - const encrypter = this.encrypter[contentEncoding] - if (!encrypter) { - throw new Error(`Encrypt not defined for ${contentEncoding}`) - } - return encrypter.encrypt(encodedPayload) - } - - /** - * Decode buffer into given type - * @param input the input buffer - * @param contentType the content type of buffer content - * @param contentEncoding the encoding type of buffer content - * @returns - */ - protected async decodeContent(input: Buffer, contentType: string, contentEncoding: string): Promise { - const decrypter = this.encrypter[contentEncoding] - if (!decrypter) { - throw new Error(`Decrypt not defined for ${contentEncoding}`) - } - - const decrypted = await decrypter.decrypt(input) - - const decoder = this.encoder[contentType] - if (!decoder) { - throw new Error(`Decode not defined for ${contentType}`) - } - return decoder.decode(decrypted) - } - - async destroy() { - if (this.channel) { - const channel = this.channel - // instruct message broker to no longer send messages - const cancelProms = this.consumerTags.map((tag) => channel.cancel(tag)) - await Promise.all(cancelProms) - - let isTimedOut = false - const timeout = setTimeout(() => { - isTimedOut = true - }, this.defaultCommandTimeout) - - // ensure actual running commands and subscriptions are finished before closing connection - const waitForExecutionEnd = () => { - if (this.pendingInvocations.size <= 0 && this.runningSubscriptionCount <= 0) { - return - } - if (isTimedOut) { - this.logger.error('Some commands or subscriptions could not finish before connection was closed') - return - } - setImmediate(waitForExecutionEnd) - } - - waitForExecutionEnd() - if (timeout) { - clearTimeout(timeout) - } - - await this.channel.close() - } - if (this.connection) { - await this.connection.close() - } - - await super.destroy() - } + protected connection?: Connection + protected channel?: Channel + + protected healthy = false + protected ready = false + + protected consumerTags: string[] = [] + + protected replyQueueName?: string + protected serviceFunctions = new Map< + string, + { + cb: (message: Command) => Promise + channel: Channel + } + >() + + protected pendingInvocations = new Map() + + protected runningSubscriptionCount = 0 + + protected subscriptions = new Map< + string, + { + cb: (message: CustomMessage) => Promise | undefined> + channel: Channel + } + >() + + protected encoder: Encoder = { + ...jsonEncoder, + } + + protected encrypter: Encrypter = { + ...plainEncrypter, + } + + constructor(config?: EventBridgeConfig) { + //= getDefaultConfig() + const conf = { + ...getDefaultConfig(), + ...config, + } + super('AmqpBridge', conf) + + this.encoder = { + ...this.encoder, + ...this.config.encoder, + } + + this.encrypter = { + ...this.encrypter, + ...this.config.encrypter, + } + } + + async isReady() { + return this.ready + } + + async isHealthy() { + return this.healthy + } + + /** + * Connect to RabbitMQ broker, ensure exchange, call back queue + */ + async start() { + await super.start() + try { + this.connection = await amqplib.connect(this.config.url ?? getDefaultConfig().url, this.config.socketOptions) + } catch (err) { + this.emit(EventBridgeEventNames.EventbridgeConnectionError, err) + this.logger.fatal({ err }, 'unable to connect to broker') + throw err + } + + this.connection.on('error', err => { + this.healthy = false + this.logger.error({ err }, 'amqp lib error') + this.emit(EventBridgeEventNames.EventbridgeError, err) + }) + this.connection.on('close', () => { + this.healthy = false + this.ready = false + this.emit(EventBridgeEventNames.EventbridgeDisconnected) + this.logger.info('amqp connection disconnected') + }) + + this.emit(EventBridgeEventNames.EventbridgeConnected) + this.logger.info('connected to broker') + this.channel = await this.connection.createChannel() + + this.channel.on('close', () => { + this.healthy = false + this.ready = false + this.logger.info('channel closed') + this.emit(EventBridgeEventNames.EventbridgeDisconnected) + }) + + this.channel.on('error', err => { + this.healthy = false + this.logger.error({ err }, 'amqp channel error') + this.emit(EventBridgeEventNames.EventbridgeError, err) + }) + + this.logger.debug('ensured: default exchange') + await this.channel.assertExchange( + this.config.exchangeName ?? getDefaultConfig().exchangeName, + 'headers', + this.config.exchangeOptions, + ) + const responseQueue = await this.channel.assertQueue('', { exclusive: true, autoDelete: true, durable: false }) + this.replyQueueName = responseQueue.queue + await this.channel.bindQueue(this.replyQueueName, this.config.exchangeName ?? getDefaultConfig().exchangeName, '', { + 'x-match': 'all', + replyTo: this.replyQueueName, + }) + const consume = await this.channel.consume( + this.replyQueueName, + async msg => { + if (!msg) { + return + } + const context = await deserializeOtpFromAmqpHeader(this.logger, msg, this.encrypter, this.encoder) + return this.startActiveSpan( + PuristaSpanName.EventBridgeCommandResponseReceived, + { kind: SpanKind.CONSUMER }, + context, + async span => { + try { + const message = await this.decodeContent( + msg.content, + msg.properties.contentType, + msg.properties.contentEncoding, + ) + + if (message.eventName) { + span.addEvent(message.eventName) + } + + const log = this.logger.getChildLogger({ customTraceId: message.traceId, ...span.spanContext() }) + + if (isCommandResponse(message)) { + const mapEntry = this.pendingInvocations.get(message.correlationId) + if (!mapEntry) { + const err = new UnhandledError( + StatusCode.BadRequest, + 'InvalidCommandResponse: received invalid command response', + getCleanedMessage(message), + ) + span.setStatus({ + code: SpanStatusCode.ERROR, + message: err.message, + }) + span.recordException(err) + log.error({ err }, 'received invalid command response') + this.emit(EventBridgeEventNames.EventbridgeError, err) + return + } + if (isCommandSuccessResponse(message)) { + mapEntry.resolve(message.payload) + } else if (isCommandErrorResponse(message)) { + const error = message.isHandledError + ? HandledError.fromMessage(message) + : UnhandledError.fromMessage(message) + span.recordException(error) + log.error({ err: error }, error.message) + mapEntry.reject(error) + } + return + } + + if (isInfoMessage(message)) { + log.trace('info message', message) + return + } + + const err = new UnhandledError(StatusCode.BadRequest, 'InvalidMessage: received invalid message', message) + span.setStatus({ + code: SpanStatusCode.ERROR, + message: err.message, + }) + span.recordException(err) + log.error({ err }, 'received invalid message') + this.emit(EventBridgeEventNames.EventbridgeError, err) + } catch (error) { + const err = new HandledError(StatusCode.InternalServerError, 'failed to handle response message', error) + span.setStatus({ + code: SpanStatusCode.ERROR, + message: err.message, + }) + span.recordException(err) + this.emit(EventBridgeEventNames.EventbridgeError, err) + this.logger.error({ err, ...span.spanContext() }, 'failed to handle response message') + } + }, + ) + }, + { noAck: true }, + ) + + this.consumerTags.push(consume.consumerTag) + + this.healthy = true + this.ready = true + this.logger.debug('ensured: response queue') + + this.logger.info('amqp event bridge ready') + } + + async emitMessage( + message: Omit, + contentType = 'application/json', + contentEncoding = 'utf-8', + ): Promise> { + const context = deserializeOtp(this.logger, message.otp) + + const name = isCommandResponse(message as EBMessage) + ? PuristaSpanName.EventBridgeCommandResponseSent + : PuristaSpanName.EventBridgeEmitMessage + + return this.startActiveSpan(name, { kind: SpanKind.PRODUCER }, context, async span => { + if (!this.channel) { + const err = new UnhandledError( + StatusCode.InternalServerError, + 'emit message: failed No channel - not connected', + ) + span.setStatus({ + code: SpanStatusCode.ERROR, + message: err.message, + }) + span.recordException(err) + this.logger.error({ err, ...span.spanContext() }, err.message) + throw err + } + + const msg = Object.freeze({ + ...message, + id: getNewEBMessageId(), + timestamp: Date.now(), + traceId: message.traceId, + otp: serializeOtp(), + sender: { + ...message.sender, + instanceId: this.instanceId, + }, + }) + + span.setAttribute(PuristaSpanTag.SenderServiceName, msg.sender.serviceName) + span.setAttribute(PuristaSpanTag.SenderServiceVersion, msg.sender.serviceVersion) + span.setAttribute(PuristaSpanTag.SenderServiceTarget, msg.sender.serviceTarget) + + if (msg.eventName) { + span.addEvent(msg.eventName) + } + + const headers: Record = { + messageType: msg.messageType, + senderServiceName: msg.sender.serviceName, + senderServiceVersion: msg.sender.serviceVersion, + senderServiceTarget: msg.sender.serviceTarget, + senderInstanceId: msg.sender.instanceId, + eventName: msg.eventName, + principalId: msg.principalId, + tenantId: msg.tenantId, + } + + serializeOtpForAmqpHeader(headers) + + const payload = await this.encodeContent(msg, contentType, contentEncoding) + + await this.channel.publish(this.config.exchangeName ?? getDefaultConfig().exchangeName, '', payload, { + messageId: msg.id, + timestamp: msg.timestamp, + contentType, + contentEncoding, + type: msg.messageType, + headers, + persistent: true, + }) + + return msg as Readonly + }) + } + + async invoke( + input: Omit, + commandTimeout: number = this.defaultCommandTimeout, + ): Promise { + const context = deserializeOtp(this.logger, input.otp) + return this.startActiveSpan( + PuristaSpanName.EventBridgeInvokeCommand, + { kind: SpanKind.PRODUCER }, + context, + async span => { + if (!this.channel) { + const err = new UnhandledError(StatusCode.InternalServerError, 'invoke failed: No channel - not connected') + span.setStatus({ + code: SpanStatusCode.ERROR, + message: err.message, + }) + span.recordException(err) + this.logger.error({ err, ...span.spanContext() }, err.message) + throw err + } + + const correlationId = getNewCorrelationId() + + const command: Command = Object.freeze({ + ...input, + id: getNewEBMessageId(), + correlationId, + timestamp: Date.now(), + messageType: EBMessageType.Command, + traceId: input.traceId, + otp: serializeOtp(), + sender: { + ...input.sender, + instanceId: this.instanceId, + }, + }) + + const removeFromPending = () => { + this.pendingInvocations.delete(correlationId) + } + + const executionPromise = new Promise((resolve, reject) => { + const timeout = setTimeout(() => { + const err = new UnhandledError( + StatusCode.GatewayTimeout, + 'invocation timed out', + undefined, + command.traceId, + ) + this.logger.warn({ err }) + rejectFn(err) + }, commandTimeout) + + const resolveFn = (successPayload: T) => { + clearTimeout(timeout) + removeFromPending() + resolve(successPayload) + } + + const rejectFn = (err: unknown) => { + clearTimeout(timeout) + removeFromPending() + reject(err) + } + + this.pendingInvocations.set(command.correlationId, { + resolve: resolveFn, + reject: rejectFn, + }) + }) + + span.setAttribute(PuristaSpanTag.SenderServiceName, command.sender.serviceName) + span.setAttribute(PuristaSpanTag.SenderServiceVersion, command.sender.serviceVersion) + span.setAttribute(PuristaSpanTag.SenderServiceTarget, command.sender.serviceTarget) + span.setAttribute(PuristaSpanTag.ReceiverServiceName, command.receiver.serviceName) + span.setAttribute(PuristaSpanTag.ReceiverServiceVersion, command.receiver.serviceVersion) + span.setAttribute(PuristaSpanTag.ReceiverServiceTarget, command.receiver.serviceTarget) + + const headers: Record = { + messageType: command.messageType, + senderServiceName: command.sender.serviceName, + senderServiceVersion: command.sender.serviceVersion, + senderServiceTarget: command.sender.serviceTarget, + senderInstanceId: command.sender.instanceId, + receiverServiceName: command.receiver.serviceName, + receiverServiceVersion: command.receiver.serviceVersion, + receiverServiceTarget: command.receiver.serviceTarget, + eventName: command.eventName, + principalId: command.principalId, + tenantId: command.tenantId, + } + serializeOtpForAmqpHeader(headers) + + const content = await this.encodeContent(command, 'application/json', 'utf-8') + + this.channel.publish(this.config.exchangeName ?? getDefaultConfig().exchangeName, '', content, { + messageId: command.id, + timestamp: command.timestamp, + correlationId: command.correlationId, + contentType: 'application/json', + contentEncoding: 'utf-8', + type: command.messageType, + headers, + replyTo: this.replyQueueName, + persistent: true, + }) + + return executionPromise + }, + ) + } + + /** + * Register a service function and ensure that there is a queue for all incoming command requests. + * @param address The service function address + * @param cb the function to call if a matching command message arrives + * @returns the id of command function queue + */ + async registerCommand( + address: EBMessageAddress, + cb: (message: Command) => Promise, + metadata: CommandDefinitionMetadataBase, + eventBridgeConfig: DefinitionEventBridgeConfig, + ): Promise { + if (!this.connection) { + throw new Error('No connection - not connected') + } + + const queueName = getCommandQueueName(address, this.config.namePrefix) + + const channel = await this.connection.createChannel() + + const noAck = eventBridgeConfig.autoacknowledge ?? true + + channel.on('close', () => { + this.healthy = false + this.logger.info({ queueName }, 'channel for command closed') + this.emit(EventBridgeEventNames.EventbridgeDisconnected) + }) + + channel.on('error', err => { + this.healthy = false + this.logger.error({ err, queueName }, 'command channel error') + this.emit(EventBridgeEventNames.EventbridgeError, err) + }) + + const queue = await channel.assertQueue(queueName, { durable: !!eventBridgeConfig.durable, autoDelete: true }) + await channel.bindQueue(queue.queue, this.config.exchangeName ?? getDefaultConfig().exchangeName, '', { + 'x-match': 'all', + messageType: EBMessageType.Command, + receiverServiceName: address.serviceName, + receiverServiceVersion: address.serviceVersion, + receiverServiceTarget: address.serviceTarget, + }) + + const consume = await channel.consume( + queue.queue, + async msg => { + const context = await deserializeOtpFromAmqpHeader(this.logger, msg, this.encrypter, this.encoder) + return this.startActiveSpan( + PuristaSpanName.EventBridgeCommandReceived, + { kind: SpanKind.CONSUMER }, + context, + async span => { + if (!msg) { + return + } + try { + const command = await this.decodeContent( + msg.content, + msg.properties.contentType, + msg.properties.contentEncoding, + ) + + command.otp = serializeOtp() + + const result = await cb(command) + + const returnContext = deserializeOtp(this.logger, result.otp) + return this.startActiveSpan( + PuristaSpanName.EventBridgeCommandResponseSent, + { kind: SpanKind.PRODUCER }, + returnContext, + async subSpan => { + const responseMessage = { + ...result, + otp: serializeOtp(), + sennder: { + ...result.sender, + instanceId: this.instanceId, + }, + } + + subSpan.setAttribute(PuristaSpanTag.SenderServiceName, responseMessage.sender.serviceName) + subSpan.setAttribute(PuristaSpanTag.SenderServiceVersion, responseMessage.sender.serviceVersion) + subSpan.setAttribute(PuristaSpanTag.SenderServiceTarget, responseMessage.sender.serviceTarget) + + if (responseMessage.eventName) { + subSpan.addEvent(responseMessage.eventName) + } + + const headers: Record = { + messageType: responseMessage.messageType, + senderServiceName: responseMessage.sender.serviceName, + senderServiceVersion: responseMessage.sender.serviceVersion, + senderServiceTarget: responseMessage.sender.serviceTarget, + senderInstanceId: responseMessage.sender.instanceId, + receiverServiceName: responseMessage.receiver.serviceName, + receiverServiceVersion: responseMessage.receiver.serviceVersion, + receiverServiceTarget: responseMessage.receiver.serviceTarget, + receiverServiceInstanceId: responseMessage.receiver.instanceId, + replyTo: msg.properties.replyTo, + eventName: responseMessage.eventName, + principalId: responseMessage.principalId, + tenantId: responseMessage.tenantId, + } + + serializeOtpForAmqpHeader(headers) + + const contentType = 'application/json' + const contentEncoding = 'utf-8' + + const payload = await this.encodeContent(responseMessage, contentType, contentEncoding) + + this.channel?.publish(this.config.exchangeName ?? getDefaultConfig().exchangeName, '', payload, { + messageId: responseMessage.id, + timestamp: responseMessage.timestamp, + correlationId: msg.properties.correlationId, + contentType, + contentEncoding, + type: responseMessage.messageType, + headers, + persistent: true, + }) + + if (!noAck) { + channel.ack(msg) + } + }, + ) + } catch (error) { + const err = new UnhandledError( + StatusCode.InternalServerError, + 'Failed to consume command response message', + { + error, + }, + ) + span.setStatus({ + code: SpanStatusCode.ERROR, + message: err.message, + }) + span.recordException(err) + this.emit(EventBridgeEventNames.EventbridgeError, err) + this.logger.error({ err }, 'Failed to consume command response message') + if (!noAck) { + channel.nack(msg) + } + } + }, + ) + }, + { noAck }, + ) + + this.consumerTags.push(consume.consumerTag) + + this.serviceFunctions.set(queueName, { cb, channel }) + + const info = createInfoMessage( + EBMessageType.InfoServiceFunctionAdded, + { ...address, instanceId: this.instanceId }, + { + payload: metadata, + }, + ) + await this.emitMessage(info) + + return queueName + } + + async unregisterCommand(address: EBMessageAddress): Promise { + try { + const queueName = getCommandQueueName(address) + const entry = this.serviceFunctions.get(queueName) + if (!entry) { + return + } + await entry.channel.close() + this.serviceFunctions.delete(queueName) + } catch (error) { + const err = new UnhandledError(StatusCode.InternalServerError, 'Failed to unregister service function', { + error, + address, + }) + this.emit(EventBridgeEventNames.EventbridgeError, err) + this.logger.error({ err }, 'Failed to unregister service function') + } + } + + async registerSubscription( + subscription: Subscription, + cb: (message: EBMessage) => Promise | undefined>, + ): Promise { + if (!this.connection) { + throw new Error('No connection - not connected') + } + + const noAck = !!subscription.eventBridgeConfig.autoacknowledge + + const isShared = subscription.eventBridgeConfig.shared === undefined || subscription.eventBridgeConfig.shared + + const queueName = isShared ? getSubscriptionQueueName(subscription.subscriber, this.config.namePrefix) : '' + + const queueOptions: amqplib.Options.AssertQueue = isShared + ? { + durable: subscription.eventBridgeConfig.durable, + } + : { exclusive: true, autoDelete: true, durable: false } + + const channel = await this.connection.createChannel() + + channel.on('close', () => { + this.healthy = false + this.logger.info({ queueName }, 'channel for subscription closed') + this.emit(EventBridgeEventNames.EventbridgeDisconnected) + }) + + channel.on('error', err => { + this.healthy = false + this.logger.error({ err, queueName }, 'subscription channel error') + this.emit(EventBridgeEventNames.EventbridgeError, err) + }) + + const queue = await channel.assertQueue(queueName, queueOptions) + await channel.bindQueue(queue.queue, this.config.exchangeName ?? getDefaultConfig().exchangeName, '', { + 'x-match': 'all', + messageType: subscription.messageType, + senderServiceName: subscription.sender?.serviceName, + senderServiceVersion: subscription.sender?.serviceVersion, + senderServiceTarget: subscription.sender?.serviceTarget, + senderInstanceId: subscription.sender?.instanceId, + receiverServiceName: subscription.receiver?.serviceName, + receiverServiceVersion: subscription.receiver?.serviceVersion, + receiverServiceTarget: subscription.receiver?.serviceTarget, + receiverInstanceId: subscription.receiver?.instanceId, + eventName: subscription.eventName, + principalId: subscription.principalId, + tenantId: subscription.tenantId, + }) + + const consume = await channel.consume( + queue.queue, + async msg => { + const context = await deserializeOtpFromAmqpHeader(this.logger, msg, this.encrypter, this.encoder) + + const spanContext = context ? trace.getSpanContext(context) : undefined + this.startActiveSpan( + PuristaSpanName.EventBridgeSubscriptionEventReceived, + { kind: SpanKind.CONSUMER, links: spanContext ? [{ context: spanContext }] : [] }, + context, + async span => { + if (!msg) { + return + } + this.runningSubscriptionCount++ + try { + const message = await this.decodeContent( + msg.content, + msg.properties.contentType, + msg.properties.contentEncoding, + ) + + span.setAttribute(PuristaSpanTag.SenderServiceName, message.sender.serviceName) + span.setAttribute(PuristaSpanTag.SenderServiceVersion, message.sender.serviceVersion) + span.setAttribute(PuristaSpanTag.SenderServiceTarget, message.sender.serviceTarget) + + if (message.eventName) { + span.addEvent(message.eventName) + } + + message.otp = serializeOtp() + + const result = await cb(message) + if (subscription.emitEventName && result) { + await this.emitMessage(result) + } + if (!noAck) { + channel.ack(msg) + } + this.runningSubscriptionCount-- + } catch (error) { + this.runningSubscriptionCount-- + const err = new UnhandledError(StatusCode.InternalServerError, 'Failed to consume subscription message', { + error, + subscription, + }) + span.setStatus({ + code: SpanStatusCode.ERROR, + message: err.message, + }) + span.recordException(err) + this.emit(EventBridgeEventNames.EventbridgeError, err) + this.logger.error({ err }, 'Failed to consume subscription message') + if (!noAck) { + channel.nack(msg) + } + } + }, + ) + }, + { noAck }, + ) + + this.consumerTags.push(consume.consumerTag) + + this.subscriptions.set(queueName, { cb, channel }) + return queueName + } + + async unregisterSubscription(address: EBMessageAddress): Promise { + try { + const queueName = getSubscriptionQueueName(address) + const entry = this.subscriptions.get(queueName) + if (!entry) { + return + } + await entry.channel.close() + this.subscriptions.delete(queueName) + } catch (error) { + const err = new UnhandledError(StatusCode.InternalServerError, 'Failed to unregister subscription', { + error, + address, + }) + this.emit(EventBridgeEventNames.EventbridgeError, err) + this.logger.error({ err }, 'Failed to unregister subscription') + } + } + + /** + * Encode given payload to buffer + * @param input + * @param contentType + * @param contentEncoding + * @returns + */ + protected async encodeContent(input: T, contentType: string, contentEncoding: string): Promise { + const encoder = this.encoder[contentType] + if (!encoder) { + throw new Error(`Encode not defined for ${contentType}`) + } + const encodedPayload = await encoder.encode(input) + + const encrypter = this.encrypter[contentEncoding] + if (!encrypter) { + throw new Error(`Encrypt not defined for ${contentEncoding}`) + } + return encrypter.encrypt(encodedPayload) + } + + /** + * Decode buffer into given type + * @param input the input buffer + * @param contentType the content type of buffer content + * @param contentEncoding the encoding type of buffer content + * @returns + */ + protected async decodeContent(input: Buffer, contentType: string, contentEncoding: string): Promise { + const decrypter = this.encrypter[contentEncoding] + if (!decrypter) { + throw new Error(`Decrypt not defined for ${contentEncoding}`) + } + + const decrypted = await decrypter.decrypt(input) + + const decoder = this.encoder[contentType] + if (!decoder) { + throw new Error(`Decode not defined for ${contentType}`) + } + return decoder.decode(decrypted) + } + + async destroy() { + if (this.channel) { + const channel = this.channel + // instruct message broker to no longer send messages + const cancelProms = this.consumerTags.map(tag => channel.cancel(tag)) + await Promise.all(cancelProms) + + let isTimedOut = false + const timeout = setTimeout(() => { + isTimedOut = true + }, this.defaultCommandTimeout) + + // ensure actual running commands and subscriptions are finished before closing connection + const waitForExecutionEnd = () => { + if (this.pendingInvocations.size <= 0 && this.runningSubscriptionCount <= 0) { + return + } + if (isTimedOut) { + this.logger.error('Some commands or subscriptions could not finish before connection was closed') + return + } + setImmediate(waitForExecutionEnd) + } + + waitForExecutionEnd() + if (timeout) { + clearTimeout(timeout) + } + + await this.channel.close() + } + if (this.connection) { + await this.connection.close() + } + + await super.destroy() + } } diff --git a/packages/amqpbridge/src/decodeContent.impl.ts b/packages/amqpbridge/src/decodeContent.impl.ts index a43d92de3..9486c1e3e 100644 --- a/packages/amqpbridge/src/decodeContent.impl.ts +++ b/packages/amqpbridge/src/decodeContent.impl.ts @@ -1,22 +1,22 @@ import type { Encoder, Encrypter } from './types/index.js' export const decodeContent = async ( - input: Buffer, - contentType: string, - contentEncoding: string, - encrypter: Encrypter, - encoder: Encoder, + input: Buffer, + contentType: string, + contentEncoding: string, + encrypter: Encrypter, + encoder: Encoder, ): Promise => { - const decrypter = encrypter[contentEncoding] - if (!decrypter) { - throw new Error(`Decrypt not defined for ${contentEncoding}`) - } + const decrypter = encrypter[contentEncoding] + if (!decrypter) { + throw new Error(`Decrypt not defined for ${contentEncoding}`) + } - const decrypted = await decrypter.decrypt(input) + const decrypted = await decrypter.decrypt(input) - const decoder = encoder[contentType] - if (!decoder) { - throw new Error(`Decode not defined for ${contentType}`) - } - return decoder.decode(decrypted) + const decoder = encoder[contentType] + if (!decoder) { + throw new Error(`Decode not defined for ${contentType}`) + } + return decoder.decode(decrypted) } diff --git a/packages/amqpbridge/src/decodeContent.test.ts b/packages/amqpbridge/src/decodeContent.test.ts index 3ed49986d..38a118bea 100644 --- a/packages/amqpbridge/src/decodeContent.test.ts +++ b/packages/amqpbridge/src/decodeContent.test.ts @@ -5,58 +5,58 @@ import { decodeContent } from './decodeContent.impl.js' import type { Encoder, Encrypter } from './types/index.js' describe('decodeContent', () => { - const sandbox: SinonSandbox = createSandbox() - - afterEach(() => { - sandbox.restore() - }) - - const inputBuffer = Buffer.from('') - const encryptedBuffer = Buffer.from('encryptedData') - const decryptedBuffer = Buffer.from('decryptedData') - const decodedObject = { message: 'Hello, world!' } - - const encryptMock = sandbox.stub().resolves(encryptedBuffer) - const decryptMock = sandbox.stub().resolves(decryptedBuffer) - - const mockEncrypter: Encrypter = { - aes256: { - encrypt: encryptMock, - decrypt: decryptMock, - }, - } - - const encodeMock = sandbox.stub().resolves(encryptedBuffer) - const decodeMock = sandbox.stub().resolves(decodedObject) - - const mockEncoder: Encoder = { - 'application/json': { - encode: encodeMock, - decode: decodeMock, - }, - } - - it('should decode encrypted and encoded content', async () => { - const result = await decodeContent(inputBuffer, 'application/json', 'aes256', mockEncrypter, mockEncoder) - - expect(result).toEqual(decodedObject) - expect(decryptMock.calledOnceWith(inputBuffer)).toBe(true) - expect(decodeMock.calledOnceWith(decryptedBuffer)).toBe(true) - }) - - it('should throw an error if decrypter is not defined', async () => { - const invalidContentEncoding = 'invalidEncoding' - - await expect( - decodeContent(inputBuffer, 'application/json', invalidContentEncoding, mockEncrypter, mockEncoder), - ).rejects.toThrow(`Decrypt not defined for ${invalidContentEncoding}`) - }) - - it('should throw an error if decoder is not defined', async () => { - const invalidContentType = 'invalidType' - - await expect(decodeContent(inputBuffer, invalidContentType, 'aes256', mockEncrypter, mockEncoder)).rejects.toThrow( - `Decode not defined for ${invalidContentType}`, - ) - }) + const sandbox: SinonSandbox = createSandbox() + + afterEach(() => { + sandbox.restore() + }) + + const inputBuffer = Buffer.from('') + const encryptedBuffer = Buffer.from('encryptedData') + const decryptedBuffer = Buffer.from('decryptedData') + const decodedObject = { message: 'Hello, world!' } + + const encryptMock = sandbox.stub().resolves(encryptedBuffer) + const decryptMock = sandbox.stub().resolves(decryptedBuffer) + + const mockEncrypter: Encrypter = { + aes256: { + encrypt: encryptMock, + decrypt: decryptMock, + }, + } + + const encodeMock = sandbox.stub().resolves(encryptedBuffer) + const decodeMock = sandbox.stub().resolves(decodedObject) + + const mockEncoder: Encoder = { + 'application/json': { + encode: encodeMock, + decode: decodeMock, + }, + } + + it('should decode encrypted and encoded content', async () => { + const result = await decodeContent(inputBuffer, 'application/json', 'aes256', mockEncrypter, mockEncoder) + + expect(result).toEqual(decodedObject) + expect(decryptMock.calledOnceWith(inputBuffer)).toBe(true) + expect(decodeMock.calledOnceWith(decryptedBuffer)).toBe(true) + }) + + it('should throw an error if decrypter is not defined', async () => { + const invalidContentEncoding = 'invalidEncoding' + + await expect( + decodeContent(inputBuffer, 'application/json', invalidContentEncoding, mockEncrypter, mockEncoder), + ).rejects.toThrow(`Decrypt not defined for ${invalidContentEncoding}`) + }) + + it('should throw an error if decoder is not defined', async () => { + const invalidContentType = 'invalidType' + + await expect(decodeContent(inputBuffer, invalidContentType, 'aes256', mockEncrypter, mockEncoder)).rejects.toThrow( + `Decode not defined for ${invalidContentType}`, + ) + }) }) diff --git a/packages/amqpbridge/src/deserializeOtpFromAmqpHeader.impl.ts b/packages/amqpbridge/src/deserializeOtpFromAmqpHeader.impl.ts index c715c72fc..37e9ffdbf 100644 --- a/packages/amqpbridge/src/deserializeOtpFromAmqpHeader.impl.ts +++ b/packages/amqpbridge/src/deserializeOtpFromAmqpHeader.impl.ts @@ -7,33 +7,33 @@ import { decodeContent } from './decodeContent.impl.js' import type { Encoder, Encrypter } from './types/index.js' export const deserializeOtpFromAmqpHeader = async ( - logger: Logger, - message: ConsumeMessage | null, - encrypter: Encrypter, - encoder: Encoder, + logger: Logger, + message: ConsumeMessage | null, + encrypter: Encrypter, + encoder: Encoder, ) => { - if (!message) { - return - } - try { - // try to use amqp header first - const header = message.properties.headers + if (!message) { + return + } + try { + // try to use amqp header first + const header = message.properties.headers - if (header['traceparent']) { - return propagation.extract(context.active(), header) - } + if (header?.traceparent) { + return propagation.extract(context.active(), header) + } - // if not present try to find in message content - const msg = await decodeContent( - message.content, - message.properties.contentType, - message.properties.contentEncoding, - encrypter, - encoder, - ) + // if not present try to find in message content + const msg = await decodeContent( + message.content, + message.properties.contentType, + message.properties.contentEncoding, + encrypter, + encoder, + ) - return deserializeOtp(logger, msg.otp) - } catch (err) { - logger.error({ err }, 'unable to deserialize otp entry from amqp header') - } + return deserializeOtp(logger, msg.otp) + } catch (err) { + logger.error({ err }, 'unable to deserialize otp entry from amqp header') + } } diff --git a/packages/amqpbridge/src/deserialzeOtpFromAmqpHeader.test.ts b/packages/amqpbridge/src/deserialzeOtpFromAmqpHeader.test.ts index ba96fd154..4f1afacf6 100644 --- a/packages/amqpbridge/src/deserialzeOtpFromAmqpHeader.test.ts +++ b/packages/amqpbridge/src/deserialzeOtpFromAmqpHeader.test.ts @@ -7,77 +7,77 @@ import { deserializeOtpFromAmqpHeader } from './deserializeOtpFromAmqpHeader.imp import { jsonEncoder, plainEncrypter } from './payloadHandling/index.js' describe('deserializeOtpFromAmqpHeader', () => { - let sandbox: SinonSandbox - - beforeEach(() => { - sandbox = createSandbox() - - sandbox.mock('./decodeContent.impl.js').returns({ - decodeContent: sandbox.stub().resolves({}), - }) - }) - - afterEach(() => { - sandbox.restore() - sandbox.reset() - }) - - it('extracts from AMQP header', async () => { - const logger = getLoggerMock(sandbox) - const encrypter = { ...plainEncrypter } - const decoder = { ...jsonEncoder } - - const traceparent = '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01' - - const content = Buffer.from( - JSON.stringify({ - ...getCommandMessageMock({ otp: JSON.stringify({ traceparent }) }), - }), - ) - - const message = { - content, - properties: { - headers: { - contentType: 'application/json', - contentEncoding: 'utf-8', - traceparent, - }, - }, - } as unknown as ConsumeMessage - - const result = await deserializeOtpFromAmqpHeader(logger.mock, message, encrypter, decoder) - - expect(result).toBeDefined() - }) - - it('extracts from message header', async () => { - const logger = getLoggerMock(sandbox) - - const encrypter = { ...plainEncrypter } - const decoder = { ...jsonEncoder } - - const traceparent = '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01' - - const message = getCommandMessageMock({ otp: JSON.stringify({ traceparent }) }) - - const content = Buffer.from( - JSON.stringify({ - ...message, - }), - ) - - const msg = { - content, - properties: { - contentType: 'application/json', - contentEncoding: 'utf-8', - headers: {}, - }, - } as unknown as ConsumeMessage - - const result = await deserializeOtpFromAmqpHeader(logger.mock, msg, encrypter, decoder) - - expect(result).toBeDefined() - }) + let sandbox: SinonSandbox + + beforeEach(() => { + sandbox = createSandbox() + + sandbox.mock('./decodeContent.impl.js').returns({ + decodeContent: sandbox.stub().resolves({}), + }) + }) + + afterEach(() => { + sandbox.restore() + sandbox.reset() + }) + + it('extracts from AMQP header', async () => { + const logger = getLoggerMock(sandbox) + const encrypter = { ...plainEncrypter } + const decoder = { ...jsonEncoder } + + const traceparent = '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01' + + const content = Buffer.from( + JSON.stringify({ + ...getCommandMessageMock({ otp: JSON.stringify({ traceparent }) }), + }), + ) + + const message = { + content, + properties: { + headers: { + contentType: 'application/json', + contentEncoding: 'utf-8', + traceparent, + }, + }, + } as unknown as ConsumeMessage + + const result = await deserializeOtpFromAmqpHeader(logger.mock, message, encrypter, decoder) + + expect(result).toBeDefined() + }) + + it('extracts from message header', async () => { + const logger = getLoggerMock(sandbox) + + const encrypter = { ...plainEncrypter } + const decoder = { ...jsonEncoder } + + const traceparent = '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01' + + const message = getCommandMessageMock({ otp: JSON.stringify({ traceparent }) }) + + const content = Buffer.from( + JSON.stringify({ + ...message, + }), + ) + + const msg = { + content, + properties: { + contentType: 'application/json', + contentEncoding: 'utf-8', + headers: {}, + }, + } as unknown as ConsumeMessage + + const result = await deserializeOtpFromAmqpHeader(logger.mock, msg, encrypter, decoder) + + expect(result).toBeDefined() + }) }) diff --git a/packages/amqpbridge/src/getCommandQueueName.impl.ts b/packages/amqpbridge/src/getCommandQueueName.impl.ts index 32c5ad907..0b11c6fca 100644 --- a/packages/amqpbridge/src/getCommandQueueName.impl.ts +++ b/packages/amqpbridge/src/getCommandQueueName.impl.ts @@ -1,10 +1,10 @@ import type { EBMessageAddress } from '@purista/core' export const getCommandQueueName = (address: EBMessageAddress, prefix?: string): string => { - let pre = '' - if (prefix?.length) { - pre = prefix.endsWith('.') ? prefix : `${prefix}.` - } + let pre = '' + if (prefix?.length) { + pre = prefix.endsWith('.') ? prefix : `${prefix}.` + } - return `${pre}cmd.${address.serviceName}.${address.serviceVersion}.${address.serviceTarget}` + return `${pre}cmd.${address.serviceName}.${address.serviceVersion}.${address.serviceTarget}` } diff --git a/packages/amqpbridge/src/getCommandQueueName.test.ts b/packages/amqpbridge/src/getCommandQueueName.test.ts index ebfe6aeab..f6ce87e21 100644 --- a/packages/amqpbridge/src/getCommandQueueName.test.ts +++ b/packages/amqpbridge/src/getCommandQueueName.test.ts @@ -1,50 +1,50 @@ import { getCommandQueueName } from './getCommandQueueName.impl.js' describe('getCommandQueueName', () => { - it('returns the correct command queue name with prefix', () => { - const address = { - serviceName: 'serviceA', - serviceVersion: '1', - serviceTarget: 'handler', - } - const prefix = 'myapp' - const expectedQueueName = 'myapp.cmd.serviceA.1.handler' - const result = getCommandQueueName(address, prefix) - expect(result).toBe(expectedQueueName) - }) + it('returns the correct command queue name with prefix', () => { + const address = { + serviceName: 'serviceA', + serviceVersion: '1', + serviceTarget: 'handler', + } + const prefix = 'myapp' + const expectedQueueName = 'myapp.cmd.serviceA.1.handler' + const result = getCommandQueueName(address, prefix) + expect(result).toBe(expectedQueueName) + }) - it('returns the correct command queue name without prefix', () => { - const address = { - serviceName: 'serviceB', - serviceVersion: '2', - serviceTarget: 'handler', - } - const expectedQueueName = 'cmd.serviceB.2.handler' - const result = getCommandQueueName(address) - expect(result).toBe(expectedQueueName) - }) + it('returns the correct command queue name without prefix', () => { + const address = { + serviceName: 'serviceB', + serviceVersion: '2', + serviceTarget: 'handler', + } + const expectedQueueName = 'cmd.serviceB.2.handler' + const result = getCommandQueueName(address) + expect(result).toBe(expectedQueueName) + }) - it('returns the correct command queue name with empty prefix', () => { - const address = { - serviceName: 'serviceC', - serviceVersion: '3', - serviceTarget: 'handler', - } - const prefix = '' - const expectedQueueName = 'cmd.serviceC.3.handler' - const result = getCommandQueueName(address, prefix) - expect(result).toBe(expectedQueueName) - }) + it('returns the correct command queue name with empty prefix', () => { + const address = { + serviceName: 'serviceC', + serviceVersion: '3', + serviceTarget: 'handler', + } + const prefix = '' + const expectedQueueName = 'cmd.serviceC.3.handler' + const result = getCommandQueueName(address, prefix) + expect(result).toBe(expectedQueueName) + }) - it('returns the correct command queue name with prefix that already ends with a dot', () => { - const address = { - serviceName: 'serviceD', - serviceVersion: '4', - serviceTarget: 'handler', - } - const prefix = 'myapp.' - const expectedQueueName = 'myapp.cmd.serviceD.4.handler' - const result = getCommandQueueName(address, prefix) - expect(result).toBe(expectedQueueName) - }) + it('returns the correct command queue name with prefix that already ends with a dot', () => { + const address = { + serviceName: 'serviceD', + serviceVersion: '4', + serviceTarget: 'handler', + } + const prefix = 'myapp.' + const expectedQueueName = 'myapp.cmd.serviceD.4.handler' + const result = getCommandQueueName(address, prefix) + expect(result).toBe(expectedQueueName) + }) }) diff --git a/packages/amqpbridge/src/getDefaultConfig.impl.ts b/packages/amqpbridge/src/getDefaultConfig.impl.ts index eab5bffd6..94b0012a5 100644 --- a/packages/amqpbridge/src/getDefaultConfig.impl.ts +++ b/packages/amqpbridge/src/getDefaultConfig.impl.ts @@ -3,13 +3,13 @@ import type { Complete } from '@purista/core' import type { AmqpBridgeConfig } from './types/index.js' export const getDefaultConfig = (): Complete & { exchangeName: string; url: string } => { - return { - exchangeName: 'purista', - namePrefix: 'purista', - url: 'amqp://localhost', - encoder: {}, - encrypter: {}, - socketOptions: undefined, - exchangeOptions: undefined, - } + return { + exchangeName: 'purista', + namePrefix: 'purista', + url: 'amqp://localhost', + encoder: {}, + encrypter: {}, + socketOptions: undefined, + exchangeOptions: undefined, + } } diff --git a/packages/amqpbridge/src/getSubscriptionQueueName.impl.ts b/packages/amqpbridge/src/getSubscriptionQueueName.impl.ts index a697e82f1..2e6d67063 100644 --- a/packages/amqpbridge/src/getSubscriptionQueueName.impl.ts +++ b/packages/amqpbridge/src/getSubscriptionQueueName.impl.ts @@ -1,10 +1,10 @@ import type { EBMessageAddress } from '@purista/core' export const getSubscriptionQueueName = (address: EBMessageAddress, prefix?: string): string => { - let pre = '' - if (prefix?.length) { - pre = prefix.endsWith('.') ? prefix : `${prefix}.` - } + let pre = '' + if (prefix?.length) { + pre = prefix.endsWith('.') ? prefix : `${prefix}.` + } - return `${pre}sub.${address.serviceName}.${address.serviceVersion}.${address.serviceTarget}` + return `${pre}sub.${address.serviceName}.${address.serviceVersion}.${address.serviceTarget}` } diff --git a/packages/amqpbridge/src/getSubscriptionQueueName.test.ts b/packages/amqpbridge/src/getSubscriptionQueueName.test.ts index 67b3724e1..a9c57dc2c 100644 --- a/packages/amqpbridge/src/getSubscriptionQueueName.test.ts +++ b/packages/amqpbridge/src/getSubscriptionQueueName.test.ts @@ -3,26 +3,26 @@ import type { EBMessageAddress } from '@purista/core' import { getSubscriptionQueueName } from './getSubscriptionQueueName.impl.js' describe('getSubscriptionQueueName', () => { - it('should return the correct subscription queue name with prefix', () => { - const address: EBMessageAddress = { - serviceName: 'example', - serviceVersion: '1.0.0', - serviceTarget: 'test', - } - const prefix = 'myprefix' - const expected = 'myprefix.sub.example.1.0.0.test' - const result = getSubscriptionQueueName(address, prefix) - expect(result).toEqual(expected) - }) + it('should return the correct subscription queue name with prefix', () => { + const address: EBMessageAddress = { + serviceName: 'example', + serviceVersion: '1.0.0', + serviceTarget: 'test', + } + const prefix = 'myprefix' + const expected = 'myprefix.sub.example.1.0.0.test' + const result = getSubscriptionQueueName(address, prefix) + expect(result).toEqual(expected) + }) - it('should return the correct subscription queue name without prefix', () => { - const address: EBMessageAddress = { - serviceName: 'example', - serviceVersion: '1.0.0', - serviceTarget: 'test', - } - const expected = 'sub.example.1.0.0.test' - const result = getSubscriptionQueueName(address) - expect(result).toEqual(expected) - }) + it('should return the correct subscription queue name without prefix', () => { + const address: EBMessageAddress = { + serviceName: 'example', + serviceVersion: '1.0.0', + serviceTarget: 'test', + } + const expected = 'sub.example.1.0.0.test' + const result = getSubscriptionQueueName(address) + expect(result).toEqual(expected) + }) }) diff --git a/packages/amqpbridge/src/index.test.ts b/packages/amqpbridge/src/index.test.ts index 398ba2425..bac57cccd 100644 --- a/packages/amqpbridge/src/index.test.ts +++ b/packages/amqpbridge/src/index.test.ts @@ -1,11 +1,11 @@ import { AmqpBridge, puristaVersion } from './index.js' describe('exports Version', () => { - it('has a version', () => { - expect(puristaVersion).toBeDefined() - }) + it('has a version', () => { + expect(puristaVersion).toBeDefined() + }) - it('exports AmqpBridge', () => { - expect(AmqpBridge).toBeDefined() - }) + it('exports AmqpBridge', () => { + expect(AmqpBridge).toBeDefined() + }) }) diff --git a/packages/amqpbridge/src/payloadHandling/jsonEncoder.ts b/packages/amqpbridge/src/payloadHandling/jsonEncoder.ts index f0d278057..05cf2e58c 100644 --- a/packages/amqpbridge/src/payloadHandling/jsonEncoder.ts +++ b/packages/amqpbridge/src/payloadHandling/jsonEncoder.ts @@ -5,8 +5,8 @@ import type { Encoder } from '../types/index.js' * Encodes JSON/JavaScript object to the AMQP message payload format and Decodes the AMQP message payload to JSON/JavaScript object */ export const jsonEncoder: Encoder = { - 'application/json': { - encode: async (input: T) => Buffer.from(JSON.stringify(input)), - decode: async (input: Buffer) => JSON.parse(input.toString()) as T, - }, + 'application/json': { + encode: async (input: T) => Buffer.from(JSON.stringify(input)), + decode: async (input: Buffer) => JSON.parse(input.toString()) as T, + }, } diff --git a/packages/amqpbridge/src/payloadHandling/plainEncrypter.ts b/packages/amqpbridge/src/payloadHandling/plainEncrypter.ts index b40a0325a..5436fb36e 100644 --- a/packages/amqpbridge/src/payloadHandling/plainEncrypter.ts +++ b/packages/amqpbridge/src/payloadHandling/plainEncrypter.ts @@ -1,8 +1,8 @@ import type { Encrypter } from '../types/index.js' export const plainEncrypter: Encrypter = { - 'utf-8': { - encrypt: async (input) => input, - decrypt: async (input) => input, - }, + 'utf-8': { + encrypt: async input => input, + decrypt: async input => input, + }, } diff --git a/packages/amqpbridge/src/serializeOtpForAmqpHeader.impl.ts b/packages/amqpbridge/src/serializeOtpForAmqpHeader.impl.ts index 553f431e5..a0b04f8f0 100644 --- a/packages/amqpbridge/src/serializeOtpForAmqpHeader.impl.ts +++ b/packages/amqpbridge/src/serializeOtpForAmqpHeader.impl.ts @@ -1,6 +1,6 @@ import { context, propagation } from '@opentelemetry/api' export const serializeOtpForAmqpHeader = (header: Record) => { - propagation.inject(context.active(), header) - return header + propagation.inject(context.active(), header) + return header } diff --git a/packages/amqpbridge/src/types/AmqpBridgeConfig.ts b/packages/amqpbridge/src/types/AmqpBridgeConfig.ts index 61200cbc6..b536d89ab 100644 --- a/packages/amqpbridge/src/types/AmqpBridgeConfig.ts +++ b/packages/amqpbridge/src/types/AmqpBridgeConfig.ts @@ -9,18 +9,18 @@ import type { Encrypter } from './Encrypter.js' * @see [amqplib documentation](https://amqp-node.github.io/amqplib/) */ export type AmqpBridgeConfig = { - /** the AMQP exchage name to be used @default purista */ - exchangeName?: string - /** the queue prefix to be used for all PURISTA queues except short living queues created by the broker on request @default purista */ - namePrefix?: string - /** the AMQP exchange options */ - exchangeOptions?: Options.AssertExchange | undefined - /** the AMQP broker url @default amqp://localhost */ - url?: string | Options.Connect - /** socket options */ - socketOptions?: any - /** the encoder(s) to be used for AMQP messages @default jsonEncoder */ - encoder?: Encoder - /** the encrypter(s) to be used for AMQP messages @default plain */ - encrypter?: Encrypter + /** the AMQP exchage name to be used @default purista */ + exchangeName?: string + /** the queue prefix to be used for all PURISTA queues except short living queues created by the broker on request @default purista */ + namePrefix?: string + /** the AMQP exchange options */ + exchangeOptions?: Options.AssertExchange | undefined + /** the AMQP broker url @default amqp://localhost */ + url?: string | Options.Connect + /** socket options */ + socketOptions?: any + /** the encoder(s) to be used for AMQP messages @default jsonEncoder */ + encoder?: Encoder + /** the encrypter(s) to be used for AMQP messages @default plain */ + encrypter?: Encrypter } diff --git a/packages/amqpbridge/src/types/EncoderFunctions.ts b/packages/amqpbridge/src/types/EncoderFunctions.ts index c7a548f5d..e0db9251b 100644 --- a/packages/amqpbridge/src/types/EncoderFunctions.ts +++ b/packages/amqpbridge/src/types/EncoderFunctions.ts @@ -1,4 +1,4 @@ export type EncoderFunctions = { - encode: (input: T) => Promise - decode: (input: Buffer) => Promise + encode: (input: T) => Promise + decode: (input: Buffer) => Promise } diff --git a/packages/amqpbridge/src/types/EncryptFunctions.ts b/packages/amqpbridge/src/types/EncryptFunctions.ts index 1af8c0bcf..03a8a45f3 100644 --- a/packages/amqpbridge/src/types/EncryptFunctions.ts +++ b/packages/amqpbridge/src/types/EncryptFunctions.ts @@ -1,4 +1,4 @@ export type EncryptFunctions = { - encrypt: (input: Buffer) => Promise - decrypt: (input: Buffer) => Promise + encrypt: (input: Buffer) => Promise + decrypt: (input: Buffer) => Promise } diff --git a/packages/amqpbridge/test/integration.test.ts b/packages/amqpbridge/test/integration.test.ts index 6f76b657a..d4404d30e 100644 --- a/packages/amqpbridge/test/integration.test.ts +++ b/packages/amqpbridge/test/integration.test.ts @@ -12,86 +12,81 @@ const AMQP_PORT = 5672 const EXAMPLE_EVENT = 'exampleEvent' describe('@purista/amqpbridge', () => { - let container: StartedTestContainer - - const sandbox = createSandbox() - const subscriptionStub = sandbox.stub().resolves() - const logger = getLoggerMock(sandbox) - const eventbridge = new AmqpBridge({ logger: logger.mock }) - const subscriptionBuilder = theServiceV1Service - .getSubscriptionBuilder('sendWelcomeEmail', 'send a welcome mail to new registered users') - .subscribeToEvent(EXAMPLE_EVENT) - .addPayloadSchema(z.any()) - .setSubscriptionFunction(subscriptionStub) - - theServiceServiceBuilder.addSubscriptionDefinition(subscriptionBuilder.getDefinition()) - - let service: Service - - beforeAll(async () => { - container = await new GenericContainer('rabbitmq:alpine') - .withLogConsumer((_stream) => { - // stream.on('data', (line) => console.debug(line)) - // eslint-disable-next-line no-console - // stream.on('err', (line) => console.error(line)) - }) - .withExposedPorts({ host: AMQP_PORT, container: AMQP_PORT }) - .start() - - await eventbridge.start() - - service = await theServiceServiceBuilder.getInstance(eventbridge, { logger: getLoggerMock(sandbox).mock }) - await service.start() - }) - - afterAll(async () => { - await service?.destroy() - await eventbridge.destroy() - await container.stop() - }) - - afterEach(() => { - sandbox.resetHistory() - }) - - it('can invoke ping command', async () => { - const command = getCommandMessageMock({ - receiver: { - serviceName: service.info.serviceName, - serviceVersion: service.info.serviceVersion, - serviceTarget: 'ping', - instanceId: 'a', - }, - sender: { - serviceName: service.info.serviceName, - serviceVersion: service.info.serviceVersion, - serviceTarget: 'some', - instanceId: 'a', - }, - payload: { - payload: undefined, - parameter: { - required: 'yes', - }, - }, - }) - const result = await eventbridge.invoke(command) - - expect(result).toEqual({ - ping: true, - }) - - expect(true).toBeTruthy() - }) - - it('receives subscriptions', async () => { - const payload = { example: 'payload' } - const commandResponse = getCommandSuccessMessageMock(payload, { eventName: EXAMPLE_EVENT }) - - await eventbridge.emitMessage(commandResponse) - - await new Promise((resolve) => setTimeout(resolve, 3000)) - - expect(subscriptionStub.called).toBeTruthy() - }) + let container: StartedTestContainer + + const sandbox = createSandbox() + const subscriptionStub = sandbox.stub().resolves() + const logger = getLoggerMock(sandbox) + const eventbridge = new AmqpBridge({ logger: logger.mock }) + const subscriptionBuilder = theServiceV1Service + .getSubscriptionBuilder('sendWelcomeEmail', 'send a welcome mail to new registered users') + .subscribeToEvent(EXAMPLE_EVENT) + .addPayloadSchema(z.any()) + .setSubscriptionFunction(subscriptionStub) + + theServiceServiceBuilder.addSubscriptionDefinition(subscriptionBuilder.getDefinition()) + + let service: Service + + beforeAll(async () => { + container = await new GenericContainer('rabbitmq:alpine') + .withExposedPorts({ host: AMQP_PORT, container: AMQP_PORT }) + .start() + + await eventbridge.start() + + service = await theServiceServiceBuilder.getInstance(eventbridge, { logger: getLoggerMock(sandbox).mock }) + await service.start() + }) + + afterAll(async () => { + await service?.destroy() + await eventbridge.destroy() + await container.stop() + }) + + afterEach(() => { + sandbox.resetHistory() + }) + + it('can invoke ping command', async () => { + const command = getCommandMessageMock({ + receiver: { + serviceName: service.info.serviceName, + serviceVersion: service.info.serviceVersion, + serviceTarget: 'ping', + instanceId: 'a', + }, + sender: { + serviceName: service.info.serviceName, + serviceVersion: service.info.serviceVersion, + serviceTarget: 'some', + instanceId: 'a', + }, + payload: { + payload: undefined, + parameter: { + required: 'yes', + }, + }, + }) + const result = await eventbridge.invoke(command) + + expect(result).toEqual({ + ping: true, + }) + + expect(true).toBeTruthy() + }) + + it('receives subscriptions', async () => { + const payload = { example: 'payload' } + const commandResponse = getCommandSuccessMessageMock(payload, { eventName: EXAMPLE_EVENT }) + + await eventbridge.emitMessage(commandResponse) + + await new Promise(resolve => setTimeout(resolve, 3000)) + + expect(subscriptionStub.called).toBeTruthy() + }) }) diff --git a/packages/amqpbridge/tsconfig.json b/packages/amqpbridge/tsconfig.json index cdb332b03..7b117ebaf 100644 --- a/packages/amqpbridge/tsconfig.json +++ b/packages/amqpbridge/tsconfig.json @@ -1,20 +1,12 @@ { - "extends": "../../tsconfig.json", - "compilerOptions": { - "outDir": "./dist", - "declaration": true, - "sourceMap": false, - "declarationMap": true, - "types": [ - "vitest/globals", - "node" - ] - }, - "include": [ - "./src/**/*", - "./test/*", - ], - "exclude": [ - "./**/*.d.ts" - ] -} \ No newline at end of file + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "./dist", + "declaration": true, + "sourceMap": false, + "declarationMap": true, + "types": ["vitest/globals", "node"] + }, + "include": ["./src/**/*", "./test/*"], + "exclude": ["./**/*.d.ts"] +} diff --git a/packages/amqpbridge/typedoc.json b/packages/amqpbridge/typedoc.json index 355bf0f98..71c4b2283 100644 --- a/packages/amqpbridge/typedoc.json +++ b/packages/amqpbridge/typedoc.json @@ -1,6 +1,5 @@ { - - "extends": ["../../typedoc.base.json"], - "entryPoints": ["src/index.ts"], - "tsconfig": "./tsconfig.json" -} \ No newline at end of file + "extends": ["../../typedoc.base.json"], + "entryPoints": ["src/index.ts"], + "tsconfig": "./tsconfig.json" +} diff --git a/packages/aws-config-store/docker-compose.yml b/packages/aws-config-store/docker-compose.yml index fb202e279..62629e91e 100644 --- a/packages/aws-config-store/docker-compose.yml +++ b/packages/aws-config-store/docker-compose.yml @@ -1,5 +1,3 @@ -version: '3' - services: aws: image: localstack/localstack:latest diff --git a/packages/aws-config-store/jsr.json b/packages/aws-config-store/jsr.json new file mode 100644 index 000000000..348a7c847 --- /dev/null +++ b/packages/aws-config-store/jsr.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://jsr.io/schema/config-file.v1.json", + "name": "@purista/aws-config-store", + "version": "1.11.0", + "description": "State store adapter for AWS System Manager", + "keywords": ["purista", "aws", "typescript", "javascript"], + "exports": "./dist/esm/index.js", + "publish": { + "include": ["dist/**/*.js", "dist/**/*.d.ts", "README.md", "package.json"], + "exclude": [ + "src", + ".github", + ".vscode", + ".zed", + "!dist", + "!dist/**/*.js", + "!dist/**/*.d.ts", + ".tshy", + ".tshy-build", + "vendor", + "docs", + "typedoc.json", + "..eslintcache", + ".npmignore" + ] + } +} diff --git a/packages/aws-config-store/package.json b/packages/aws-config-store/package.json index f43fc4267..5be43e287 100644 --- a/packages/aws-config-store/package.json +++ b/packages/aws-config-store/package.json @@ -1,66 +1,63 @@ { - "name": "@purista/aws-config-store", - "version": "1.11.0", - "description": "State store adapter for AWS System Manager", - "homepage": "https://purista.dev", - "repository": { - "type": "git", - "url": "git@github.com:sebastianwessel/purista.git" - }, - "author": "Sebastian Wessel", - "license": "ISC", - "type": "module", - "main": "./dist/commonjs/index.js", - "exports": { - "./package.json": "./package.json", - ".": { - "import": { - "types": "./dist/esm/index.d.ts", - "default": "./dist/esm/index.js" - }, - "require": { - "types": "./dist/commonjs/index.d.ts", - "default": "./dist/commonjs/index.js" - } - } - }, - "files": [ - "dist/**/*" - ], - "publishConfig": { - "access": "public" - }, - "engines": { - "node": ">=18.15" - }, - "scripts": { - "lint": "eslint . --ext .ts,.json --cache . --fix", - "test": "vitest", - "build": "rimraf dist && tshy", - "env:up": "docker compose -f docker-compose.yml up -d", - "env:down": "docker compose -f docker-compose.yml down" - }, - "tshy": { - "exclude": [ - "src/**/*.test.ts" - ], - "exports": { - "./package.json": "./package.json", - ".": "./src/index.ts" - } - }, - "devDependencies": { - "@types/node": "^20.11.17", - "@types/sinon": "^17.0.3", - "sinon": "^17.0.1", - "testcontainers": "^10.6.0", - "tshy": "^1.11.1", - "vitest": "^1.3.0" - }, - "dependencies": { - "@aws-sdk/client-ssm": "^3.515.0", - "@purista/core": "*" - }, - "peerDependenciesMeta": {}, - "types": "./dist/commonjs/index.d.ts" + "name": "@purista/aws-config-store", + "version": "1.11.0", + "description": "State store adapter for AWS System Manager", + "homepage": "https://purista.dev", + "repository": { + "type": "git", + "url": "git@github.com:puristajs/purista.git" + }, + "author": "Sebastian Wessel", + "license": "ISC", + "type": "module", + "main": "./dist/commonjs/index.js", + "exports": { + "./package.json": "./package.json", + ".": { + "import": { + "types": "./dist/esm/index.d.ts", + "default": "./dist/esm/index.js" + }, + "require": { + "types": "./dist/commonjs/index.d.ts", + "default": "./dist/commonjs/index.js" + } + } + }, + "files": ["dist/**/*"], + "publishConfig": { + "access": "public" + }, + "engines": { + "node": ">=18.15" + }, + "scripts": { + "lint": "npx @biomejs/biome check --write", + "test": "vitest", + "build": "rimraf dist && tshy", + "env:up": "docker compose -f docker-compose.yml up -d", + "env:down": "docker compose -f docker-compose.yml down" + }, + "tshy": { + "exclude": ["src/**/*.test.ts"], + "exports": { + "./package.json": "./package.json", + ".": "./src/index.ts" + } + }, + "devDependencies": { + "@types/node": "^22.5.1", + "@types/sinon": "^17.0.3", + "sinon": "^19.0.2", + "testcontainers": "^10.12.0", + "tshy": "^3.0.2", + "vitest": "^3.0.4" + }, + "dependencies": { + "@aws-sdk/client-ssm": "^3.620.0", + "@purista/core": "*" + }, + "peerDependenciesMeta": {}, + "types": "./dist/commonjs/index.d.ts", + "module": "./dist/esm/index.js" } diff --git a/packages/aws-config-store/src/AWSConfigStore.impl.ts b/packages/aws-config-store/src/AWSConfigStore.impl.ts index 084179ee1..28a4d1b90 100644 --- a/packages/aws-config-store/src/AWSConfigStore.impl.ts +++ b/packages/aws-config-store/src/AWSConfigStore.impl.ts @@ -1,10 +1,10 @@ import { - DeleteParameterCommand, - GetParameterCommand, - ParameterNotFound, - ParameterType, - PutParameterCommand, - SSMClient, + DeleteParameterCommand, + GetParameterCommand, + ParameterNotFound, + ParameterType, + PutParameterCommand, + SSMClient, } from '@aws-sdk/client-ssm' import type { ObjectWithKeysFromStringArray, StoreBaseConfig } from '@purista/core' import { ConfigStoreBaseClass, StatusCode, UnhandledError } from '@purista/core' @@ -25,52 +25,52 @@ import type { AWSConfigStoreConfig } from './types.js' * It will be removed/overwritten on next get request. */ export class AWSConfigStore extends ConfigStoreBaseClass { - client: SSMClient + client: SSMClient - constructor(config: StoreBaseConfig) { - super('AWSConfigStore', { enableCache: true, ...config }) - this.client = new SSMClient(this.config.client) - } + constructor(config: StoreBaseConfig) { + super('AWSConfigStore', { enableCache: true, ...config }) + this.client = new SSMClient(this.config.client) + } - protected async getConfigImpl( - ...configNames: ConfigNames - ): Promise> { - const result: Record = {} + protected async getConfigImpl( + ...configNames: ConfigNames + ): Promise> { + const result: Record = {} - for (const name of configNames) { - try { - const command = new GetParameterCommand({ - Name: name, - }) - const res = await this.client.send(command) - result[name] = res.Parameter?.Value - } catch (err) { - if (!(err instanceof ParameterNotFound)) { - throw UnhandledError.fromError(err, StatusCode.InternalServerError) - } - result[name] = undefined - } - } + for (const name of configNames) { + try { + const command = new GetParameterCommand({ + Name: name, + }) + const res = await this.client.send(command) + result[name] = res.Parameter?.Value + } catch (err) { + if (!(err instanceof ParameterNotFound)) { + throw UnhandledError.fromError(err, StatusCode.InternalServerError) + } + result[name] = undefined + } + } - return result as ObjectWithKeysFromStringArray - } + return result as ObjectWithKeysFromStringArray + } - protected async removeConfigImpl(configName: string) { - const command = new DeleteParameterCommand({ - Name: configName, - }) + protected async removeConfigImpl(configName: string) { + const command = new DeleteParameterCommand({ + Name: configName, + }) - await this.client.send(command) - } + await this.client.send(command) + } - protected async setConfigImpl(configName: string, configValue: string) { - const command = new PutParameterCommand({ - Name: configName, - Value: configValue, - Type: ParameterType.STRING, - Overwrite: true, - }) + protected async setConfigImpl(configName: string, configValue: string) { + const command = new PutParameterCommand({ + Name: configName, + Value: configValue, + Type: ParameterType.STRING, + Overwrite: true, + }) - await this.client.send(command) - } + await this.client.send(command) + } } diff --git a/packages/aws-config-store/src/index.test.ts b/packages/aws-config-store/src/index.test.ts index e928e685e..c0dbd4dd1 100644 --- a/packages/aws-config-store/src/index.test.ts +++ b/packages/aws-config-store/src/index.test.ts @@ -1,11 +1,11 @@ import { AWSConfigStore, puristaVersion } from './index.js' describe('exports AWSConfigStore', () => { - it('has a version', () => { - expect(puristaVersion).toBeDefined() - }) + it('has a version', () => { + expect(puristaVersion).toBeDefined() + }) - it('exports AWSSecretStore', () => { - expect(AWSConfigStore).toBeDefined() - }) + it('exports AWSSecretStore', () => { + expect(AWSConfigStore).toBeDefined() + }) }) diff --git a/packages/aws-config-store/src/types.ts b/packages/aws-config-store/src/types.ts index 53373924e..8aff8c83d 100644 --- a/packages/aws-config-store/src/types.ts +++ b/packages/aws-config-store/src/types.ts @@ -4,8 +4,8 @@ import type { SSMClientConfig } from '@aws-sdk/client-ssm' * AWS System Manager config */ export type AWSConfigStoreConfig = { - /** - * AWS client options - */ - client: SSMClientConfig + /** + * AWS client options + */ + client: SSMClientConfig } diff --git a/packages/aws-config-store/test/integration.test.ts b/packages/aws-config-store/test/integration.test.ts index 4c33bd667..52d749b23 100644 --- a/packages/aws-config-store/test/integration.test.ts +++ b/packages/aws-config-store/test/integration.test.ts @@ -6,51 +6,51 @@ import { getLoggerMock } from '@purista/core' import { AWSConfigStore } from '../src/AWSConfigStore.impl.js' describe('AWS Config Manager config store', () => { - beforeAll(async () => { - execSync(`cd ${resolve(__dirname, '../')} && npm run env:up`) - - await new Promise((resolve) => { - setTimeout(() => { - resolve(undefined) - }, 5000) - }) - }) - - afterAll(async () => { - execSync(`cd ${resolve(__dirname, '../')} && npm run env:down`) - }) - - const store = new AWSConfigStore({ - enableGet: true, - enableRemove: true, - enableSet: true, - logger: getLoggerMock().mock, - client: { - endpoint: 'http://localhost:4567', - region: 'us-east-1', - credentials: { - accessKeyId: 'test', - secretAccessKey: 'test', - }, - }, - }) - - it('set a config key', async () => { - await expect(store.setConfig('test', 'my-value')).resolves.toBeUndefined() - }) - - it('gets a config key', async () => { - await expect(store.getConfig('test')).resolves.toStrictEqual({ test: 'my-value' }) - }) - - it('updates a config key', async () => { - await expect(store.setConfig('test', 'my-value-updated')).resolves.toBeUndefined() - await expect(store.getConfig('test')).resolves.toStrictEqual({ test: 'my-value-updated' }) - }) - - it('removes a config key', async () => { - await expect(store.getConfig('test')).resolves.toStrictEqual({ test: 'my-value-updated' }) - await expect(store.removeConfig('test')).resolves.toBeUndefined() - await expect(store.getConfig('test')).resolves.toStrictEqual({ test: undefined }) - }) + beforeAll(async () => { + execSync(`cd ${resolve(__dirname, '../')} && npm run env:up`) + + await new Promise(resolve => { + setTimeout(() => { + resolve(undefined) + }, 5000) + }) + }) + + afterAll(async () => { + execSync(`cd ${resolve(__dirname, '../')} && npm run env:down`) + }) + + const store = new AWSConfigStore({ + enableGet: true, + enableRemove: true, + enableSet: true, + logger: getLoggerMock().mock, + client: { + endpoint: 'http://localhost:4567', + region: 'us-east-1', + credentials: { + accessKeyId: 'test', + secretAccessKey: 'test', + }, + }, + }) + + it('set a config key', async () => { + await expect(store.setConfig('test', 'my-value')).resolves.toBeUndefined() + }) + + it('gets a config key', async () => { + await expect(store.getConfig('test')).resolves.toStrictEqual({ test: 'my-value' }) + }) + + it('updates a config key', async () => { + await expect(store.setConfig('test', 'my-value-updated')).resolves.toBeUndefined() + await expect(store.getConfig('test')).resolves.toStrictEqual({ test: 'my-value-updated' }) + }) + + it('removes a config key', async () => { + await expect(store.getConfig('test')).resolves.toStrictEqual({ test: 'my-value-updated' }) + await expect(store.removeConfig('test')).resolves.toBeUndefined() + await expect(store.getConfig('test')).resolves.toStrictEqual({ test: undefined }) + }) }) diff --git a/packages/aws-config-store/tsconfig.json b/packages/aws-config-store/tsconfig.json index ff68a176a..37678fdd4 100644 --- a/packages/aws-config-store/tsconfig.json +++ b/packages/aws-config-store/tsconfig.json @@ -1,20 +1,12 @@ { - "extends": "../../tsconfig.json", - "compilerOptions": { - "outDir": "./dist", - "declaration": true, - "sourceMap": false, - "declarationMap": true, - "types": [ - "vitest/globals", - "node" - ] - }, - "exclude": [ - "./**/*.d.ts" - ], - "include": [ - "./src/**/*", - "./test/*", - ], -} \ No newline at end of file + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "./dist", + "declaration": true, + "sourceMap": false, + "declarationMap": true, + "types": ["vitest/globals", "node"] + }, + "exclude": ["./**/*.d.ts"], + "include": ["./src/**/*", "./test/*"] +} diff --git a/packages/aws-config-store/typedoc.json b/packages/aws-config-store/typedoc.json index 355bf0f98..71c4b2283 100644 --- a/packages/aws-config-store/typedoc.json +++ b/packages/aws-config-store/typedoc.json @@ -1,6 +1,5 @@ { - - "extends": ["../../typedoc.base.json"], - "entryPoints": ["src/index.ts"], - "tsconfig": "./tsconfig.json" -} \ No newline at end of file + "extends": ["../../typedoc.base.json"], + "entryPoints": ["src/index.ts"], + "tsconfig": "./tsconfig.json" +} diff --git a/packages/aws-secret-store/docker-compose.yml b/packages/aws-secret-store/docker-compose.yml index fadd25ac2..b3350a471 100644 --- a/packages/aws-secret-store/docker-compose.yml +++ b/packages/aws-secret-store/docker-compose.yml @@ -1,5 +1,3 @@ -version: '3' - services: aws: image: localstack/localstack:latest diff --git a/packages/aws-secret-store/jsr.json b/packages/aws-secret-store/jsr.json new file mode 100644 index 000000000..2cbe3469d --- /dev/null +++ b/packages/aws-secret-store/jsr.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://jsr.io/schema/config-file.v1.json", + "name": "@purista/aws-secret-store", + "version": "1.11.0", + "description": "State store adapter for AWS Secrets Manager", + "keywords": ["purista", "aws", "typescript", "javascript"], + "exports": "./dist/esm/index.js", + "publish": { + "include": ["dist/**/*.js", "dist/**/*.d.ts", "README.md", "package.json"], + "exclude": [ + "src", + ".github", + ".vscode", + ".zed", + "!dist", + "!dist/**/*.js", + "!dist/**/*.d.ts", + ".tshy", + ".tshy-build", + "vendor", + "docs", + "typedoc.json", + "..eslintcache", + ".npmignore" + ] + } +} diff --git a/packages/aws-secret-store/package.json b/packages/aws-secret-store/package.json index a36ff146c..c673ab499 100644 --- a/packages/aws-secret-store/package.json +++ b/packages/aws-secret-store/package.json @@ -1,66 +1,63 @@ { - "name": "@purista/aws-secret-store", - "version": "1.11.0", - "description": "State store adapter for AWS Secrets Manager", - "homepage": "https://purista.dev", - "repository": { - "type": "git", - "url": "git@github.com:sebastianwessel/purista.git" - }, - "author": "Sebastian Wessel", - "license": "ISC", - "type": "module", - "main": "./dist/commonjs/index.js", - "exports": { - "./package.json": "./package.json", - ".": { - "import": { - "types": "./dist/esm/index.d.ts", - "default": "./dist/esm/index.js" - }, - "require": { - "types": "./dist/commonjs/index.d.ts", - "default": "./dist/commonjs/index.js" - } - } - }, - "files": [ - "dist/**/*" - ], - "publishConfig": { - "access": "public" - }, - "engines": { - "node": ">=18.15" - }, - "scripts": { - "lint": "eslint . --ext .ts,.json --cache . --fix", - "test": "vitest", - "build": "rimraf dist && tshy", - "env:up": "docker compose -f docker-compose.yml up -d", - "env:down": "docker compose -f docker-compose.yml down" - }, - "tshy": { - "exclude": [ - "src/**/*.test.ts" - ], - "exports": { - "./package.json": "./package.json", - ".": "./src/index.ts" - } - }, - "devDependencies": { - "@types/node": "^20.11.17", - "@types/sinon": "^17.0.3", - "sinon": "^17.0.1", - "testcontainers": "^10.6.0", - "tshy": "^1.11.1", - "vitest": "^1.3.0" - }, - "dependencies": { - "@aws-sdk/client-secrets-manager": "^3.515.0", - "@purista/core": "*" - }, - "peerDependenciesMeta": {}, - "types": "./dist/commonjs/index.d.ts" + "name": "@purista/aws-secret-store", + "version": "1.11.0", + "description": "State store adapter for AWS Secrets Manager", + "homepage": "https://purista.dev", + "repository": { + "type": "git", + "url": "git@github.com:puristajs/purista.git" + }, + "author": "Sebastian Wessel", + "license": "ISC", + "type": "module", + "main": "./dist/commonjs/index.js", + "exports": { + "./package.json": "./package.json", + ".": { + "import": { + "types": "./dist/esm/index.d.ts", + "default": "./dist/esm/index.js" + }, + "require": { + "types": "./dist/commonjs/index.d.ts", + "default": "./dist/commonjs/index.js" + } + } + }, + "files": ["dist/**/*"], + "publishConfig": { + "access": "public" + }, + "engines": { + "node": ">=18.15" + }, + "scripts": { + "lint": "npx @biomejs/biome check --write", + "test": "vitest", + "build": "rimraf dist && tshy", + "env:up": "docker compose -f docker-compose.yml up -d", + "env:down": "docker compose -f docker-compose.yml down" + }, + "tshy": { + "exclude": ["src/**/*.test.ts"], + "exports": { + "./package.json": "./package.json", + ".": "./src/index.ts" + } + }, + "devDependencies": { + "@types/node": "^22.5.1", + "@types/sinon": "^17.0.3", + "sinon": "^19.0.2", + "testcontainers": "^10.12.0", + "tshy": "^3.0.2", + "vitest": "^3.0.4" + }, + "dependencies": { + "@aws-sdk/client-secrets-manager": "^3.620.0", + "@purista/core": "*" + }, + "peerDependenciesMeta": {}, + "types": "./dist/commonjs/index.d.ts", + "module": "./dist/esm/index.js" } diff --git a/packages/aws-secret-store/src/AWSSecretStore.impl.ts b/packages/aws-secret-store/src/AWSSecretStore.impl.ts index db3c783e0..417427b75 100644 --- a/packages/aws-secret-store/src/AWSSecretStore.impl.ts +++ b/packages/aws-secret-store/src/AWSSecretStore.impl.ts @@ -1,17 +1,17 @@ import { - CreateSecretCommand, - DeleteSecretCommand, - GetSecretValueCommand, - ResourceNotFoundException, - SecretsManagerClient, - UpdateSecretCommand, + CreateSecretCommand, + DeleteSecretCommand, + GetSecretValueCommand, + ResourceNotFoundException, + SecretsManagerClient, + UpdateSecretCommand, } from '@aws-sdk/client-secrets-manager' import { - type ObjectWithKeysFromStringArray, - SecretStoreBaseClass, - StatusCode, - type StoreBaseConfig, - UnhandledError, + type ObjectWithKeysFromStringArray, + SecretStoreBaseClass, + StatusCode, + type StoreBaseConfig, + UnhandledError, } from '@purista/core' import type { AWSSecretStoreConfig } from './types.js' @@ -30,69 +30,68 @@ import type { AWSSecretStoreConfig } from './types.js' * It will be removed/overwritten on next get request. */ export class AWSSecretStore extends SecretStoreBaseClass { - client: SecretsManagerClient + client: SecretsManagerClient - constructor(config: StoreBaseConfig) { - super('AWSSecretStore', { enableCache: true, ...config }) - this.client = new SecretsManagerClient(this.config.client) - } + constructor(config: StoreBaseConfig) { + super('AWSSecretStore', { enableCache: true, ...config }) + this.client = new SecretsManagerClient(this.config.client) + } - protected async getSecretImpl( - ...secretNames: SecretNames - ): Promise> { - const result: Record = {} + protected async getSecretImpl( + ...secretNames: SecretNames + ): Promise> { + const result: Record = {} - for (const name of secretNames) { - try { - const command = new GetSecretValueCommand({ - SecretId: name, - }) - const res = await this.client.send(command) - result[name] = res.SecretString - } catch (err) { - if (!(err instanceof ResourceNotFoundException)) { - throw UnhandledError.fromError(err, StatusCode.InternalServerError) - } else { - result[name] = undefined - } - } - } + for (const name of secretNames) { + try { + const command = new GetSecretValueCommand({ + SecretId: name, + }) + const res = await this.client.send(command) + result[name] = res.SecretString + } catch (err) { + if (!(err instanceof ResourceNotFoundException)) { + throw UnhandledError.fromError(err, StatusCode.InternalServerError) + } + result[name] = undefined + } + } - return result as ObjectWithKeysFromStringArray - } + return result as ObjectWithKeysFromStringArray + } - protected async removeSecretImpl(secretName: string) { - const command = new DeleteSecretCommand({ - SecretId: secretName, - }) + protected async removeSecretImpl(secretName: string) { + const command = new DeleteSecretCommand({ + SecretId: secretName, + }) - await this.client.send(command) - } + await this.client.send(command) + } - protected async setSecretImpl(secretName: string, secretValue: string) { - try { - const command = new UpdateSecretCommand({ - SecretId: secretName, - SecretString: secretValue, - }) + protected async setSecretImpl(secretName: string, secretValue: string) { + try { + const command = new UpdateSecretCommand({ + SecretId: secretName, + SecretString: secretValue, + }) - await this.client.send(command) - } catch (err) { - if (err instanceof ResourceNotFoundException) { - const createCommand = new CreateSecretCommand({ - Name: secretName, - SecretString: secretValue, - }) + await this.client.send(command) + } catch (err) { + if (err instanceof ResourceNotFoundException) { + const createCommand = new CreateSecretCommand({ + Name: secretName, + SecretString: secretValue, + }) - await this.client.send(createCommand) + await this.client.send(createCommand) - const command = new UpdateSecretCommand({ - SecretId: secretName, - SecretString: secretValue, - }) + const command = new UpdateSecretCommand({ + SecretId: secretName, + SecretString: secretValue, + }) - await this.client.send(command) - } - } - } + await this.client.send(command) + } + } + } } diff --git a/packages/aws-secret-store/src/index.test.ts b/packages/aws-secret-store/src/index.test.ts index 05eb5f991..37ec41d15 100644 --- a/packages/aws-secret-store/src/index.test.ts +++ b/packages/aws-secret-store/src/index.test.ts @@ -1,11 +1,11 @@ import { AWSSecretStore, puristaVersion } from './index.js' describe('exports AWSSecretStore', () => { - it('has a version', () => { - expect(puristaVersion).toBeDefined() - }) + it('has a version', () => { + expect(puristaVersion).toBeDefined() + }) - it('exports AWSSecretStore', () => { - expect(AWSSecretStore).toBeDefined() - }) + it('exports AWSSecretStore', () => { + expect(AWSSecretStore).toBeDefined() + }) }) diff --git a/packages/aws-secret-store/src/types.ts b/packages/aws-secret-store/src/types.ts index 9acc33c94..971c7e753 100644 --- a/packages/aws-secret-store/src/types.ts +++ b/packages/aws-secret-store/src/types.ts @@ -4,8 +4,8 @@ import type { SecretsManagerClientConfigType } from '@aws-sdk/client-secrets-man * AWS Secret Manager store config */ export type AWSSecretStoreConfig = { - /** - * AWS client options - */ - client: SecretsManagerClientConfigType + /** + * AWS client options + */ + client: SecretsManagerClientConfigType } diff --git a/packages/aws-secret-store/test/integration.test.ts b/packages/aws-secret-store/test/integration.test.ts index 0bf1d2f1e..a0bbcbe5b 100644 --- a/packages/aws-secret-store/test/integration.test.ts +++ b/packages/aws-secret-store/test/integration.test.ts @@ -6,51 +6,51 @@ import { getLoggerMock } from '@purista/core' import { AWSSecretStore } from '../src/AWSSecretStore.impl.js' describe('AWS Secret Manager secret store', () => { - beforeAll(async () => { - execSync(`cd ${resolve(__dirname, '../')} && npm run env:up`) - - await new Promise((resolve) => { - setTimeout(() => { - resolve(undefined) - }, 5000) - }) - }) - - afterAll(async () => { - execSync(`cd ${resolve(__dirname, '../')} && npm run env:down`) - }) - - const store = new AWSSecretStore({ - enableGet: true, - enableRemove: true, - enableSet: true, - logger: getLoggerMock().mock, - client: { - endpoint: 'http://localhost:4566', - region: 'us-east-1', - credentials: { - accessKeyId: 'test', - secretAccessKey: 'test', - }, - }, - }) - - it('set a secret key', async () => { - await expect(store.setSecret('test', 'my-value')).resolves.toBeUndefined() - }) - - it('gets a secret key', async () => { - await expect(store.getSecret('test')).resolves.toStrictEqual({ test: 'my-value' }) - }) - - it('updates a secret key', async () => { - await expect(store.setSecret('test', 'my-value-updated')).resolves.toBeUndefined() - await expect(store.getSecret('test')).resolves.toStrictEqual({ test: 'my-value-updated' }) - }) - - it('removes a secret key', async () => { - await expect(store.getSecret('test')).resolves.toStrictEqual({ test: 'my-value-updated' }) - await expect(store.removeSecret('test')).resolves.toBeUndefined() - await expect(store.getSecret('test')).resolves.toStrictEqual({ test: undefined }) - }) + beforeAll(async () => { + execSync(`cd ${resolve(__dirname, '../')} && npm run env:up`) + + await new Promise(resolve => { + setTimeout(() => { + resolve(undefined) + }, 5000) + }) + }) + + afterAll(async () => { + execSync(`cd ${resolve(__dirname, '../')} && npm run env:down`) + }) + + const store = new AWSSecretStore({ + enableGet: true, + enableRemove: true, + enableSet: true, + logger: getLoggerMock().mock, + client: { + endpoint: 'http://localhost:4566', + region: 'us-east-1', + credentials: { + accessKeyId: 'test', + secretAccessKey: 'test', + }, + }, + }) + + it('set a secret key', async () => { + await expect(store.setSecret('test', 'my-value')).resolves.toBeUndefined() + }) + + it('gets a secret key', async () => { + await expect(store.getSecret('test')).resolves.toStrictEqual({ test: 'my-value' }) + }) + + it('updates a secret key', async () => { + await expect(store.setSecret('test', 'my-value-updated')).resolves.toBeUndefined() + await expect(store.getSecret('test')).resolves.toStrictEqual({ test: 'my-value-updated' }) + }) + + it('removes a secret key', async () => { + await expect(store.getSecret('test')).resolves.toStrictEqual({ test: 'my-value-updated' }) + await expect(store.removeSecret('test')).resolves.toBeUndefined() + await expect(store.getSecret('test')).resolves.toStrictEqual({ test: undefined }) + }) }) diff --git a/packages/aws-secret-store/tsconfig.json b/packages/aws-secret-store/tsconfig.json index ff68a176a..37678fdd4 100644 --- a/packages/aws-secret-store/tsconfig.json +++ b/packages/aws-secret-store/tsconfig.json @@ -1,20 +1,12 @@ { - "extends": "../../tsconfig.json", - "compilerOptions": { - "outDir": "./dist", - "declaration": true, - "sourceMap": false, - "declarationMap": true, - "types": [ - "vitest/globals", - "node" - ] - }, - "exclude": [ - "./**/*.d.ts" - ], - "include": [ - "./src/**/*", - "./test/*", - ], -} \ No newline at end of file + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "./dist", + "declaration": true, + "sourceMap": false, + "declarationMap": true, + "types": ["vitest/globals", "node"] + }, + "exclude": ["./**/*.d.ts"], + "include": ["./src/**/*", "./test/*"] +} diff --git a/packages/aws-secret-store/typedoc.json b/packages/aws-secret-store/typedoc.json index 355bf0f98..71c4b2283 100644 --- a/packages/aws-secret-store/typedoc.json +++ b/packages/aws-secret-store/typedoc.json @@ -1,6 +1,5 @@ { - - "extends": ["../../typedoc.base.json"], - "entryPoints": ["src/index.ts"], - "tsconfig": "./tsconfig.json" -} \ No newline at end of file + "extends": ["../../typedoc.base.json"], + "entryPoints": ["src/index.ts"], + "tsconfig": "./tsconfig.json" +} diff --git a/packages/azure-secret-store/docker-compose.yml b/packages/azure-secret-store/docker-compose.yml index 54771c0bf..d65211367 100644 --- a/packages/azure-secret-store/docker-compose.yml +++ b/packages/azure-secret-store/docker-compose.yml @@ -1,16 +1,14 @@ -version: "2" - services: lowkey-vault: container_name: lowkey-vault-nodejs - image: nagyesta/lowkey-vault:2.1.47@sha256:d3255e0447373124aa4cdebe978724d8890a03c85515dbae71ea1e84e1bcbf75 + image: nagyesta/lowkey-vault:2.4.42 ports: - "8443:8443" environment: LOWKEY_ARGS: "--server.port=8443" assumed-identity: container_name: assumed-identity-nodejs - image: nagyesta/assumed-identity:1.1.16@sha256:ecb60ed6d5426d1c67e8d0e61079a9445c688d895023ebed9d8b2324325ca837 + image: nagyesta/assumed-identity:1.2.16 ports: - "8081:8081" environment: diff --git a/packages/azure-secret-store/jsr.json b/packages/azure-secret-store/jsr.json new file mode 100644 index 000000000..3cefbd656 --- /dev/null +++ b/packages/azure-secret-store/jsr.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://jsr.io/schema/config-file.v1.json", + "name": "@purista/azure-secret-store", + "version": "1.11.0", + "description": "State store adapter for Azure Key Vault", + "keywords": ["purista", "aws", "typescript", "javascript"], + "exports": "./dist/esm/index.js", + "publish": { + "include": ["dist/**/*.js", "dist/**/*.d.ts", "README.md", "package.json"], + "exclude": [ + "src", + ".github", + ".vscode", + ".zed", + "!dist", + "!dist/**/*.js", + "!dist/**/*.d.ts", + ".tshy", + ".tshy-build", + "vendor", + "docs", + "typedoc.json", + "..eslintcache", + ".npmignore" + ] + } +} diff --git a/packages/azure-secret-store/package.json b/packages/azure-secret-store/package.json index f77133be8..7684c8511 100644 --- a/packages/azure-secret-store/package.json +++ b/packages/azure-secret-store/package.json @@ -1,68 +1,65 @@ { - "name": "@purista/azure-secret-store", - "version": "1.11.0", - "description": "State store adapter for Azure Key Vault", - "homepage": "https://purista.dev", - "repository": { - "type": "git", - "url": "git@github.com:sebastianwessel/purista.git" - }, - "author": "Sebastian Wessel", - "license": "ISC", - "type": "module", - "main": "./dist/commonjs/index.js", - "exports": { - "./package.json": "./package.json", - ".": { - "import": { - "types": "./dist/esm/index.d.ts", - "default": "./dist/esm/index.js" - }, - "require": { - "types": "./dist/commonjs/index.d.ts", - "default": "./dist/commonjs/index.js" - } - } - }, - "files": [ - "dist/**/*" - ], - "publishConfig": { - "access": "public" - }, - "engines": { - "node": ">=18.15" - }, - "scripts": { - "lint": "eslint . --ext .ts,.json --cache . --fix", - "test": "vitest", - "build": "rimraf dist && tshy", - "env:up": "docker compose -f docker-compose.yml up -d", - "env:down": "docker compose -f docker-compose.yml down" - }, - "tshy": { - "exclude": [ - "src/**/*.test.ts" - ], - "exports": { - "./package.json": "./package.json", - ".": "./src/index.ts" - } - }, - "devDependencies": { - "@types/node": "^20.11.17", - "@types/sinon": "^17.0.3", - "dotenv": "^16.4.4", - "sinon": "^17.0.1", - "testcontainers": "^10.6.0", - "tshy": "^1.11.1", - "vitest": "^1.3.0" - }, - "dependencies": { - "@azure/identity": "^4.0.1", - "@azure/keyvault-secrets": "^4.8.0", - "@purista/core": "*" - }, - "peerDependenciesMeta": {}, - "types": "./dist/commonjs/index.d.ts" + "name": "@purista/azure-secret-store", + "version": "1.11.0", + "description": "State store adapter for Azure Key Vault", + "homepage": "https://purista.dev", + "repository": { + "type": "git", + "url": "git@github.com:puristajs/purista.git" + }, + "author": "Sebastian Wessel", + "license": "ISC", + "type": "module", + "main": "./dist/commonjs/index.js", + "exports": { + "./package.json": "./package.json", + ".": { + "import": { + "types": "./dist/esm/index.d.ts", + "default": "./dist/esm/index.js" + }, + "require": { + "types": "./dist/commonjs/index.d.ts", + "default": "./dist/commonjs/index.js" + } + } + }, + "files": ["dist/**/*"], + "publishConfig": { + "access": "public" + }, + "engines": { + "node": ">=18.15" + }, + "scripts": { + "lint": "npx @biomejs/biome check --write", + "test": "vitest", + "build": "rimraf dist && tshy", + "env:up": "docker compose -f docker-compose.yml up -d", + "env:down": "docker compose -f docker-compose.yml down" + }, + "tshy": { + "exclude": ["src/**/*.test.ts"], + "exports": { + "./package.json": "./package.json", + ".": "./src/index.ts" + } + }, + "devDependencies": { + "@types/node": "^22.5.1", + "@types/sinon": "^17.0.3", + "dotenv": "^16.4.5", + "sinon": "^19.0.2", + "testcontainers": "^10.12.0", + "tshy": "^3.0.2", + "vitest": "^3.0.4" + }, + "dependencies": { + "@azure/identity": "^4.3.0", + "@azure/keyvault-secrets": "^4.8.0", + "@purista/core": "*" + }, + "peerDependenciesMeta": {}, + "types": "./dist/commonjs/index.d.ts", + "module": "./dist/esm/index.js" } diff --git a/packages/azure-secret-store/src/AzureSecretStore.impl.ts b/packages/azure-secret-store/src/AzureSecretStore.impl.ts index e400c646c..eb2577bbf 100644 --- a/packages/azure-secret-store/src/AzureSecretStore.impl.ts +++ b/packages/azure-secret-store/src/AzureSecretStore.impl.ts @@ -1,11 +1,11 @@ import { DefaultAzureCredential } from '@azure/identity' import { SecretClient } from '@azure/keyvault-secrets' import { - type ObjectWithKeysFromStringArray, - SecretStoreBaseClass, - StatusCode, - type StoreBaseConfig, - UnhandledError, + type ObjectWithKeysFromStringArray, + SecretStoreBaseClass, + StatusCode, + type StoreBaseConfig, + UnhandledError, } from '@purista/core' import type { AzureSecretStoreConfig } from './types.js' @@ -24,40 +24,40 @@ import type { AzureSecretStoreConfig } from './types.js' * It will be removed/overwritten on next get request. */ export class AzureSecretStore extends SecretStoreBaseClass { - client: SecretClient - - constructor(config: StoreBaseConfig) { - super('AzureSecretStore', { enableCache: true, ...config }) - - const credential = new DefaultAzureCredential() - - this.client = new SecretClient(this.config.vaultUrl, credential, this.config.options) - } - - protected async getSecretImpl( - ...secretNames: SecretNames - ): Promise> { - const result: Record = {} - - for (const name of secretNames) { - try { - const response = await this.client.getSecret(name) - result[name] = response?.value - } catch (err) { - result[name] = undefined - this.logger.error({ err }) - throw UnhandledError.fromError(err, StatusCode.InternalServerError) - } - } - - return result as ObjectWithKeysFromStringArray - } - - protected async removeSecretImpl(secretName: string) { - await this.client.beginDeleteSecret(secretName) - } - - protected async setSecretImpl(secretName: string, secretValue: string) { - await this.client.setSecret(secretName, secretValue) - } + client: SecretClient + + constructor(config: StoreBaseConfig) { + super('AzureSecretStore', { enableCache: true, ...config }) + + const credential = new DefaultAzureCredential() + + this.client = new SecretClient(this.config.vaultUrl, credential, this.config.options) + } + + protected async getSecretImpl( + ...secretNames: SecretNames + ): Promise> { + const result: Record = {} + + for (const name of secretNames) { + try { + const response = await this.client.getSecret(name) + result[name] = response?.value + } catch (err) { + result[name] = undefined + this.logger.error({ err }) + throw UnhandledError.fromError(err, StatusCode.InternalServerError) + } + } + + return result as ObjectWithKeysFromStringArray + } + + protected async removeSecretImpl(secretName: string) { + await this.client.beginDeleteSecret(secretName) + } + + protected async setSecretImpl(secretName: string, secretValue: string) { + await this.client.setSecret(secretName, secretValue) + } } diff --git a/packages/azure-secret-store/src/index.test.ts b/packages/azure-secret-store/src/index.test.ts index 44bb0da67..993ab4f22 100644 --- a/packages/azure-secret-store/src/index.test.ts +++ b/packages/azure-secret-store/src/index.test.ts @@ -1,11 +1,11 @@ import { AzureSecretStore, puristaVersion } from './index.js' describe('exports AzureSecretStore', () => { - it('has a version', () => { - expect(puristaVersion).toBeDefined() - }) + it('has a version', () => { + expect(puristaVersion).toBeDefined() + }) - it('exports AzureSecretStore', () => { - expect(AzureSecretStore).toBeDefined() - }) + it('exports AzureSecretStore', () => { + expect(AzureSecretStore).toBeDefined() + }) }) diff --git a/packages/azure-secret-store/src/types.ts b/packages/azure-secret-store/src/types.ts index 82d27b273..fac8ece75 100644 --- a/packages/azure-secret-store/src/types.ts +++ b/packages/azure-secret-store/src/types.ts @@ -4,10 +4,10 @@ import type { SecretClientOptions } from '@azure/keyvault-secrets' * Azure Key Vault store config */ export type AzureSecretStoreConfig = { - /** - * The URL to reach the Azure Key Vault - * @example https://[KEY_VAULT_NAME].vault.azure.net - */ - vaultUrl: string - options?: SecretClientOptions + /** + * The URL to reach the Azure Key Vault + * @example https://[KEY_VAULT_NAME].vault.azure.net + */ + vaultUrl: string + options?: SecretClientOptions } diff --git a/packages/azure-secret-store/test/integration.test.ts b/packages/azure-secret-store/test/integration.test.ts index 3ac40c2b3..b802271d6 100644 --- a/packages/azure-secret-store/test/integration.test.ts +++ b/packages/azure-secret-store/test/integration.test.ts @@ -8,52 +8,52 @@ import { stub } from 'sinon' import { AzureSecretStore } from '../src/AzureSecretStore.impl.js' describe.skip('Azure Secret Manager secret store', () => { - let store: AzureSecretStore - beforeAll(async () => { - execSync(`cd ${resolve(__dirname, '../')} && npm run env:up`) - - await new Promise((resolve) => { - setTimeout(() => { - resolve(undefined) - }, 5000) - }) - - // temporary workaround as assumed-identity-nodejs does not work by setting AZURE_POD_IDENTITY_AUTHORITY_HOST - stub(DefaultAzureCredential.prototype, 'getToken').resolves({ - expiresOnTimestamp: new Date().getTime() + 30000, - token: 'noop', - }) - - store = new AzureSecretStore({ - enableGet: true, - enableRemove: true, - enableSet: true, - logger: getLoggerMock().mock, - vaultUrl: 'https://localhost:8443', - options: { serviceVersion: '7.4', disableChallengeResourceVerification: true }, - }) - }) - - afterAll(async () => { - execSync(`cd ${resolve(__dirname, '../')} && npm run env:down`) - }) - - it('set a secret key', async () => { - await expect(store.setSecret('test', 'my-value')).resolves.toBeUndefined() - }) - - it('gets a secret key', async () => { - await expect(store.getSecret('test')).resolves.toStrictEqual({ test: 'my-value' }) - }) - - it('updates a secret key', async () => { - await expect(store.setSecret('test', 'my-value-updated')).resolves.toBeUndefined() - await expect(store.getSecret('test')).resolves.toStrictEqual({ test: 'my-value-updated' }) - }) - - it('removes a secret key', async () => { - await expect(store.getSecret('test')).resolves.toStrictEqual({ test: 'my-value-updated' }) - await expect(store.removeSecret('test')).resolves.toBeUndefined() - await expect(store.getSecret('test')).resolves.toStrictEqual({ test: undefined }) - }) + let store: AzureSecretStore + beforeAll(async () => { + execSync(`cd ${resolve(__dirname, '../')} && npm run env:up`) + + await new Promise(resolve => { + setTimeout(() => { + resolve(undefined) + }, 5000) + }) + + // temporary workaround as assumed-identity-nodejs does not work by setting AZURE_POD_IDENTITY_AUTHORITY_HOST + stub(DefaultAzureCredential.prototype, 'getToken').resolves({ + expiresOnTimestamp: new Date().getTime() + 30000, + token: 'noop', + }) + + store = new AzureSecretStore({ + enableGet: true, + enableRemove: true, + enableSet: true, + logger: getLoggerMock().mock, + vaultUrl: 'https://localhost:8443', + options: { serviceVersion: '7.4', disableChallengeResourceVerification: true }, + }) + }) + + afterAll(async () => { + execSync(`cd ${resolve(__dirname, '../')} && npm run env:down`) + }) + + it('set a secret key', async () => { + await expect(store.setSecret('test', 'my-value')).resolves.toBeUndefined() + }) + + it('gets a secret key', async () => { + await expect(store.getSecret('test')).resolves.toStrictEqual({ test: 'my-value' }) + }) + + it('updates a secret key', async () => { + await expect(store.setSecret('test', 'my-value-updated')).resolves.toBeUndefined() + await expect(store.getSecret('test')).resolves.toStrictEqual({ test: 'my-value-updated' }) + }) + + it('removes a secret key', async () => { + await expect(store.getSecret('test')).resolves.toStrictEqual({ test: 'my-value-updated' }) + await expect(store.removeSecret('test')).resolves.toBeUndefined() + await expect(store.getSecret('test')).resolves.toStrictEqual({ test: undefined }) + }) }) diff --git a/packages/azure-secret-store/tsconfig.json b/packages/azure-secret-store/tsconfig.json index ff68a176a..37678fdd4 100644 --- a/packages/azure-secret-store/tsconfig.json +++ b/packages/azure-secret-store/tsconfig.json @@ -1,20 +1,12 @@ { - "extends": "../../tsconfig.json", - "compilerOptions": { - "outDir": "./dist", - "declaration": true, - "sourceMap": false, - "declarationMap": true, - "types": [ - "vitest/globals", - "node" - ] - }, - "exclude": [ - "./**/*.d.ts" - ], - "include": [ - "./src/**/*", - "./test/*", - ], -} \ No newline at end of file + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "./dist", + "declaration": true, + "sourceMap": false, + "declarationMap": true, + "types": ["vitest/globals", "node"] + }, + "exclude": ["./**/*.d.ts"], + "include": ["./src/**/*", "./test/*"] +} diff --git a/packages/azure-secret-store/typedoc.json b/packages/azure-secret-store/typedoc.json index 355bf0f98..71c4b2283 100644 --- a/packages/azure-secret-store/typedoc.json +++ b/packages/azure-secret-store/typedoc.json @@ -1,6 +1,5 @@ { - - "extends": ["../../typedoc.base.json"], - "entryPoints": ["src/index.ts"], - "tsconfig": "./tsconfig.json" -} \ No newline at end of file + "extends": ["../../typedoc.base.json"], + "entryPoints": ["src/index.ts"], + "tsconfig": "./tsconfig.json" +} diff --git a/packages/base-http-bridge/jsr.json b/packages/base-http-bridge/jsr.json new file mode 100644 index 000000000..11bba4709 --- /dev/null +++ b/packages/base-http-bridge/jsr.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://jsr.io/schema/config-file.v1.json", + "name": "@purista/base-http-bridge", + "version": "1.11.0", + "description": "HTTP base eventbridge core functions for PURISTA backend framework", + "keywords": ["purista", "http", "typescript", "javascript"], + "exports": "./dist/esm/index.js", + "publish": { + "include": ["dist/**/*.js", "dist/**/*.d.ts", "README.md", "package.json"], + "exclude": [ + "src", + ".github", + ".vscode", + ".zed", + "!dist", + "!dist/**/*.js", + "!dist/**/*.d.ts", + ".tshy", + ".tshy-build", + "vendor", + "docs", + "typedoc.json", + "..eslintcache", + ".npmignore" + ] + } +} diff --git a/packages/base-http-bridge/package.json b/packages/base-http-bridge/package.json index 4e9c7b69f..8d2df0e5c 100644 --- a/packages/base-http-bridge/package.json +++ b/packages/base-http-bridge/package.json @@ -1,63 +1,62 @@ { - "name": "@purista/base-http-bridge", - "version": "1.11.0", - "description": "HTTP base eventbridge core functions for PURISTA backend framework", - "homepage": "https://purista.dev", - "repository": { - "type": "git", - "url": "git@github.com:sebastianwessel/purista.git" - }, - "author": "Sebastian Wessel", - "license": "ISC", - "type": "module", - "publishConfig": { - "access": "public" - }, - "engines": { - "node": ">=18.15" - }, - "scripts": { - "lint": "eslint . --ext .ts,.json --cache . --fix", - "test": "vitest", - "build": "rimraf dist && tshy" - }, - "tshy": { - "exclude": [ - "src/**/*.test.ts" - ], - "exports": { - "./package.json": "./package.json", - ".": "./src/index.ts" - } - }, - "devDependencies": { - "@types/node": "^20.11.17", - "@types/sinon": "^17.0.3", - "sinon": "^17.0.1", - "tshy": "^1.11.1", - "vitest": "^1.3.0" - }, - "dependencies": { - "@opentelemetry/api": "^1.7.0", - "@opentelemetry/semantic-conventions": "^1.19.0", - "@purista/core": "*", - "cloudevents": "^8.0.0", - "hono": "^4.0.4" - }, - "peerDependenciesMeta": {}, - "exports": { - "./package.json": "./package.json", - ".": { - "import": { - "types": "./dist/esm/index.d.ts", - "default": "./dist/esm/index.js" - }, - "require": { - "types": "./dist/commonjs/index.d.ts", - "default": "./dist/commonjs/index.js" - } - } - }, - "main": "./dist/commonjs/index.js", - "types": "./dist/commonjs/index.d.ts" + "name": "@purista/base-http-bridge", + "version": "1.11.0", + "description": "HTTP base eventbridge core functions for PURISTA backend framework", + "homepage": "https://purista.dev", + "repository": { + "type": "git", + "url": "git@github.com:puristajs/purista.git" + }, + "author": "Sebastian Wessel", + "license": "ISC", + "type": "module", + "publishConfig": { + "access": "public" + }, + "engines": { + "node": ">=18.15" + }, + "scripts": { + "lint": "npx @biomejs/biome check --write", + "test": "vitest", + "build": "rimraf dist && tshy" + }, + "tshy": { + "exclude": ["src/**/*.test.ts"], + "exports": { + "./package.json": "./package.json", + ".": "./src/index.ts" + } + }, + "devDependencies": { + "@types/node": "^22.5.1", + "@types/sinon": "^17.0.3", + "sinon": "^19.0.2", + "tshy": "^3.0.2", + "vitest": "^3.0.4" + }, + "dependencies": { + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/semantic-conventions": "^1.27.0", + "@purista/core": "*", + "cloudevents": "^8.0.2", + "hono": "^4.4.7" + }, + "peerDependenciesMeta": {}, + "exports": { + "./package.json": "./package.json", + ".": { + "import": { + "types": "./dist/esm/index.d.ts", + "default": "./dist/esm/index.js" + }, + "require": { + "types": "./dist/commonjs/index.d.ts", + "default": "./dist/commonjs/index.js" + } + } + }, + "main": "./dist/commonjs/index.js", + "types": "./dist/commonjs/index.d.ts", + "module": "./dist/esm/index.js" } diff --git a/packages/base-http-bridge/src/HttpEventBridge/HttpEventBridge.impl.ts b/packages/base-http-bridge/src/HttpEventBridge/HttpEventBridge.impl.ts index 03dd0bd04..611801b30 100644 --- a/packages/base-http-bridge/src/HttpEventBridge/HttpEventBridge.impl.ts +++ b/packages/base-http-bridge/src/HttpEventBridge/HttpEventBridge.impl.ts @@ -2,43 +2,43 @@ import { Server } from 'node:http' import type { Http2SecureServer, Http2Server } from 'node:http2' -import { context, propagation, SpanKind, SpanStatusCode } from '@opentelemetry/api' +import { SpanKind, SpanStatusCode, context, propagation } from '@opentelemetry/api' import type { - Command, - CommandErrorResponse, - CommandResponse, - CommandSuccessResponse, - CustomMessage, - DefinitionEventBridgeConfig, - EBMessage, - EBMessageAddress, - EventBridge, - EventBridgeConfig, - HttpExposedServiceMeta, - Subscription, + Command, + CommandErrorResponse, + CommandResponse, + CommandSuccessResponse, + CustomMessage, + DefinitionEventBridgeConfig, + EBMessage, + EBMessageAddress, + EventBridge, + EventBridgeConfig, + HttpExposedServiceMeta, + Subscription, } from '@purista/core' import { - deserializeOtp, - EBMessageType, - EventBridgeBaseClass, - EventBridgeEventNames, - getErrorMessageForCode, - getNewCorrelationId, - getNewEBMessageId, - HandledError, - isCommandErrorResponse, - isHttpExposedServiceMeta, - isInfoMessage, - PuristaSpanName, - PuristaSpanTag, - serializeOtp, - StatusCode, - UnhandledError, + EBMessageType, + EventBridgeBaseClass, + EventBridgeEventNames, + HandledError, + PuristaSpanName, + PuristaSpanTag, + StatusCode, + UnhandledError, + deserializeOtp, + getErrorMessageForCode, + getNewCorrelationId, + getNewEBMessageId, + isCommandErrorResponse, + isHttpExposedServiceMeta, + isInfoMessage, + serializeOtp, } from '@purista/core' import { Hono } from 'hono' import { compress } from 'hono/compress' import { PatternRouter } from 'hono/router/pattern-router' -import type { StatusCode as HonoStatusCode } from 'hono/utils/http-status' +import type { ContentfulStatusCode } from 'hono/utils/http-status' import { getCommandHandler } from './getCommandHandler.impl.js' import { getCommandHandlerRestApi } from './getCommandHandlerRestApi.impl.js' @@ -63,303 +63,303 @@ import type { HttpEventBridgeClient, HttpEventBridgeConfig } from './types/index * - trouter */ export class HttpEventBridge - extends EventBridgeBaseClass - // eslint-disable-next-line prettier/prettier - implements EventBridge { - public server: Server | Http2Server | Http2SecureServer | undefined - public app: Hono - public isShuttingDown = false - public isStarted = false - - public client: HttpEventBridgeClient - - constructor(config: EventBridgeConfig, client: HttpEventBridgeClient) { - const defaults = getDefaultHttpEventBridgeConfig() - const conf = { - ...defaults, - ...config, - } - - super(conf.name ?? 'HttpEventBridge', conf) - - this.client = client - - this.app = new Hono({ router: new PatternRouter() }) - } - - async start() { - this.app.notFound((c) => { - const err = new HandledError(StatusCode.NotFound, getErrorMessageForCode(StatusCode.NotFound), { - method: c.req.method, - path: c.req.path, - url: c.req.url, - }) - - this.logger.error({ err }, err.message) - - return c.json(err.getErrorResponse(), err.errorCode as HonoStatusCode) - }) - - this.app.onError((err, c) => { - this.logger.error({ err }, err.message) - const responseError = UnhandledError.fromError(err) - return c.json(responseError.getErrorResponse(), responseError.errorCode as any) - }) - - if (this.config.enableHttpCompression) { - this.app.use('*', compress()) - } - - this.app.use('*', async (c, next) => { - if (this.isShuttingDown) { - const err = { message: 'shut down in progress', status: StatusCode.ServiceUnavailable } - return new Response(JSON.stringify(err), { - status: err.status, - statusText: getErrorMessageForCode(err.status), - headers: { - 'content-type': 'application/json; charset=utf-8', - }, - }) - } - await next() - }) - - this.app.get('/healthz', healthzRoute) - - this.server = this.config.serve({ - fetch: this.app.fetch, - port: this.config.serverPort, - hostname: this.config.serverHost, - }) - - this.server.on('listening', () => { - this.emit(EventBridgeEventNames.EventbridgeConnected) - }) - - this.server.on('close', () => { - this.emit(EventBridgeEventNames.EventbridgeDisconnected) - }) - - this.server.on('error', (err) => { - this.emit(EventBridgeEventNames.EventbridgeError, err) - }) - } - - async emitMessage( - message: Omit, - ): Promise> { - const currentContext = deserializeOtp(this.logger, message.otp) - - return this.startActiveSpan( - PuristaSpanName.EventBridgeEmitMessage, - { kind: SpanKind.PRODUCER }, - currentContext, - async (span) => { - const msg = Object.freeze({ - ...message, - sender: { - ...message.sender, - instanceId: this.instanceId, - }, - id: getNewEBMessageId(), - timestamp: Date.now(), - traceId: message.traceId, - otp: serializeOtp(), - }) - - if (isInfoMessage(msg as EBMessage)) { - this.logger.debug('skipping info message') - return msg as Readonly - } - - if (!msg.eventName) { - const err = new UnhandledError(StatusCode.BadRequest, 'message must contain a event name') - this.logger.error({ err }, err.message) - span.recordException(err) - - span.setStatus({ - code: SpanStatusCode.ERROR, - message: err.message, - }) - throw err - } - - span.setAttribute(PuristaSpanTag.SenderServiceName, msg.sender.serviceName) - span.setAttribute(PuristaSpanTag.SenderServiceVersion, msg.sender.serviceVersion) - span.setAttribute(PuristaSpanTag.SenderServiceTarget, msg.sender.serviceTarget) - - span.addEvent(msg.eventName) - - const headers: Record = {} - propagation.inject(context.active(), headers) - - try { - await this.client.sendEvent(msg as EBMessage) - } catch (err) { - this.emit(EventBridgeEventNames.EventbridgeError, err) - throw err - } - - return msg as Readonly - }, - ) - } - - async invoke( - input: Omit, - ttl?: number, - ): Promise { - const currentContext = deserializeOtp(this.logger, input.otp) - return this.startActiveSpan(PuristaSpanName.EventBridgeInvokeCommand, {}, currentContext, async (span) => { - const command: Command = Object.freeze({ - ...input, - sender: { - ...input.sender, - instanceId: this.instanceId, - }, - id: getNewEBMessageId(), - correlationId: getNewCorrelationId(), - timestamp: Date.now(), - messageType: EBMessageType.Command, - traceId: input.traceId, - otp: serializeOtp(), - }) - - span.setAttribute(PuristaSpanTag.SenderServiceName, command.sender.serviceName) - span.setAttribute(PuristaSpanTag.SenderServiceVersion, command.sender.serviceVersion) - span.setAttribute(PuristaSpanTag.SenderServiceTarget, command.sender.serviceTarget) - span.setAttribute(PuristaSpanTag.ReceiverServiceName, command.receiver.serviceName) - span.setAttribute(PuristaSpanTag.ReceiverServiceVersion, command.receiver.serviceVersion) - span.setAttribute(PuristaSpanTag.ReceiverServiceTarget, command.receiver.serviceTarget) - - const headers: Record = {} - propagation.inject(context.active(), headers) - - let message: CommandResponse - try { - message = await this.client.invoke(command, headers, ttl) - } catch (error) { - this.emit(EventBridgeEventNames.EventbridgeError, error) - throw error - } - - if (isCommandErrorResponse(message)) { - const err = message.isHandledError ? HandledError.fromMessage(message) : UnhandledError.fromMessage(message) - this.logger.error({ err }, err.message) - span.recordException(err) - span.setStatus({ - code: SpanStatusCode.ERROR, - message: err.message, - }) - - throw err - } - - return message.payload as T - }) - } - - async registerCommand( - address: EBMessageAddress, - cb: ( - message: Command, - ) => Promise< - Readonly> | Readonly> - >, - metadata: HttpExposedServiceMeta, - eventBridgeConfig: DefinitionEventBridgeConfig, - ): Promise { - const fn = getCommandHandler.bind(this) - const handler = fn(address, cb, metadata, eventBridgeConfig, this.config.commandPayloadAsCloudEvent) - - const path = this.client.getInternalPathForCommand(address) - - this.app.post(path, handler) - this.logger.debug({ path }, 'command added') - - if (isHttpExposedServiceMeta(metadata) && this.config.enableRestApiExpose) { - const httpMeta = metadata.expose.http - const apiPath = this.client.getApiPathForCommand(address, metadata) - - this.logger.debug({ apiPath }) - - const fnRest = getCommandHandlerRestApi.bind(this) - const handlerRest = fnRest(address, cb, metadata, eventBridgeConfig) - - switch (httpMeta.method) { - case 'DELETE': - this.app.delete(apiPath, handlerRest) - break - case 'GET': - this.app.get(apiPath, handlerRest) - break - case 'PATCH': - this.app.patch(apiPath, handlerRest) - break - case 'POST': - this.app.post(apiPath, handlerRest) - break - case 'PUT': - this.app.put(apiPath, handlerRest) - break - } - - this.logger.debug({ path, method: httpMeta.method }, 'command added') - } - return path - } - - async unregisterCommand(address: EBMessageAddress): Promise { - this.logger.debug({ address }, 'unregister command') - } - - async registerSubscription( - subscription: Subscription, - cb: (message: EBMessage) => Promise | undefined>, - ): Promise { - if (this.isStarted) { - throw new UnhandledError( - StatusCode.InternalServerError, - 'subscriptions must be registered before starting the http event bridge', - ) - } - - const fn = getSubscriptionHandler.bind(this) - const handler = fn(subscription, cb, this.config.subscriptionPayloadAsCloudEvent) - - const path = this.client.getInternalPathForSubscription(subscription.subscriber) - - this.app.post(path, handler) - this.logger.debug({ path }, 'subscription added') - return path - } - - async unregisterSubscription(address: EBMessageAddress): Promise { - this.logger.debug({ address }, 'unregister subscription') - } - - async isReady(): Promise { - return this.isStarted && !this.isShuttingDown - } - - async isHealthy(): Promise { - if (!this.isStarted) { - return false - } - return this.client.isSidecarAvailable() - } - - /** - * Shut down event bridge as gracefully as possible - */ - async destroy(): Promise { - if (!this.server) { - return - } - if (this.server instanceof Server) { - this.server.closeIdleConnections() - } - const server = this.server - await new Promise((resolve) => server.close(resolve)) - } + extends EventBridgeBaseClass + implements EventBridge +{ + public server: Server | Http2Server | Http2SecureServer | undefined + public app: Hono + public isShuttingDown = false + public isStarted = false + + public client: HttpEventBridgeClient + + constructor(config: EventBridgeConfig, client: HttpEventBridgeClient) { + const defaults = getDefaultHttpEventBridgeConfig() + const conf = { + ...defaults, + ...config, + } + + super(conf.name ?? 'HttpEventBridge', conf) + + this.client = client + + this.app = new Hono({ router: new PatternRouter() }) + } + + async start() { + this.app.notFound(c => { + const err = new HandledError(StatusCode.NotFound, getErrorMessageForCode(StatusCode.NotFound), { + method: c.req.method, + path: c.req.path, + url: c.req.url, + }) + + this.logger.error({ err }, err.message) + + return c.json(err.getErrorResponse(), err.errorCode as ContentfulStatusCode) + }) + + this.app.onError((err, c) => { + this.logger.error({ err }, err.message) + const responseError = UnhandledError.fromError(err) + return c.json(responseError.getErrorResponse(), responseError.errorCode as any) + }) + + if (this.config.enableHttpCompression) { + this.app.use('*', compress()) + } + + this.app.use('*', async (c, next) => { + if (this.isShuttingDown) { + const err = { message: 'shut down in progress', status: StatusCode.ServiceUnavailable } + return new Response(JSON.stringify(err), { + status: err.status, + statusText: getErrorMessageForCode(err.status), + headers: { + 'content-type': 'application/json; charset=utf-8', + }, + }) + } + await next() + }) + + this.app.get('/healthz', healthzRoute) + + this.server = this.config.serve({ + fetch: this.app.fetch, + port: this.config.serverPort, + hostname: this.config.serverHost, + }) + + this.server.on('listening', () => { + this.emit(EventBridgeEventNames.EventbridgeConnected) + }) + + this.server.on('close', () => { + this.emit(EventBridgeEventNames.EventbridgeDisconnected) + }) + + this.server.on('error', err => { + this.emit(EventBridgeEventNames.EventbridgeError, err) + }) + } + + async emitMessage( + message: Omit, + ): Promise> { + const currentContext = deserializeOtp(this.logger, message.otp) + + return this.startActiveSpan( + PuristaSpanName.EventBridgeEmitMessage, + { kind: SpanKind.PRODUCER }, + currentContext, + async span => { + const msg = Object.freeze({ + ...message, + sender: { + ...message.sender, + instanceId: this.instanceId, + }, + id: getNewEBMessageId(), + timestamp: Date.now(), + traceId: message.traceId, + otp: serializeOtp(), + }) + + if (isInfoMessage(msg as EBMessage)) { + this.logger.debug('skipping info message') + return msg as Readonly + } + + if (!msg.eventName) { + const err = new UnhandledError(StatusCode.BadRequest, 'message must contain a event name') + this.logger.error({ err }, err.message) + span.recordException(err) + + span.setStatus({ + code: SpanStatusCode.ERROR, + message: err.message, + }) + throw err + } + + span.setAttribute(PuristaSpanTag.SenderServiceName, msg.sender.serviceName) + span.setAttribute(PuristaSpanTag.SenderServiceVersion, msg.sender.serviceVersion) + span.setAttribute(PuristaSpanTag.SenderServiceTarget, msg.sender.serviceTarget) + + span.addEvent(msg.eventName) + + const headers: Record = {} + propagation.inject(context.active(), headers) + + try { + await this.client.sendEvent(msg as EBMessage) + } catch (err) { + this.emit(EventBridgeEventNames.EventbridgeError, err) + throw err + } + + return msg as Readonly + }, + ) + } + + async invoke( + input: Omit, + ttl?: number, + ): Promise { + const currentContext = deserializeOtp(this.logger, input.otp) + return this.startActiveSpan(PuristaSpanName.EventBridgeInvokeCommand, {}, currentContext, async span => { + const command: Command = Object.freeze({ + ...input, + sender: { + ...input.sender, + instanceId: this.instanceId, + }, + id: getNewEBMessageId(), + correlationId: getNewCorrelationId(), + timestamp: Date.now(), + messageType: EBMessageType.Command, + traceId: input.traceId, + otp: serializeOtp(), + }) + + span.setAttribute(PuristaSpanTag.SenderServiceName, command.sender.serviceName) + span.setAttribute(PuristaSpanTag.SenderServiceVersion, command.sender.serviceVersion) + span.setAttribute(PuristaSpanTag.SenderServiceTarget, command.sender.serviceTarget) + span.setAttribute(PuristaSpanTag.ReceiverServiceName, command.receiver.serviceName) + span.setAttribute(PuristaSpanTag.ReceiverServiceVersion, command.receiver.serviceVersion) + span.setAttribute(PuristaSpanTag.ReceiverServiceTarget, command.receiver.serviceTarget) + + const headers: Record = {} + propagation.inject(context.active(), headers) + + let message: CommandResponse + try { + message = await this.client.invoke(command, headers, ttl) + } catch (error) { + this.emit(EventBridgeEventNames.EventbridgeError, error) + throw error + } + + if (isCommandErrorResponse(message)) { + const err = message.isHandledError ? HandledError.fromMessage(message) : UnhandledError.fromMessage(message) + this.logger.error({ err }, err.message) + span.recordException(err) + span.setStatus({ + code: SpanStatusCode.ERROR, + message: err.message, + }) + + throw err + } + + return message.payload as T + }) + } + + async registerCommand( + address: EBMessageAddress, + cb: ( + message: Command, + ) => Promise< + Readonly> | Readonly> + >, + metadata: HttpExposedServiceMeta, + eventBridgeConfig: DefinitionEventBridgeConfig, + ): Promise { + const fn = getCommandHandler.bind(this) + const handler = fn(address, cb, metadata, eventBridgeConfig, this.config.commandPayloadAsCloudEvent) + + const path = this.client.getInternalPathForCommand(address) + + this.app.post(path, handler) + this.logger.debug({ path }, 'command added') + + if (isHttpExposedServiceMeta(metadata) && this.config.enableRestApiExpose) { + const httpMeta = metadata.expose.http + const apiPath = this.client.getApiPathForCommand(address, metadata) + + this.logger.debug({ apiPath }) + + const fnRest = getCommandHandlerRestApi.bind(this) + const handlerRest = fnRest(address, cb, metadata, eventBridgeConfig) + + switch (httpMeta.method) { + case 'DELETE': + this.app.delete(apiPath, handlerRest) + break + case 'GET': + this.app.get(apiPath, handlerRest) + break + case 'PATCH': + this.app.patch(apiPath, handlerRest) + break + case 'POST': + this.app.post(apiPath, handlerRest) + break + case 'PUT': + this.app.put(apiPath, handlerRest) + break + } + + this.logger.debug({ path, method: httpMeta.method }, 'command added') + } + return path + } + + async unregisterCommand(address: EBMessageAddress): Promise { + this.logger.debug({ address }, 'unregister command') + } + + async registerSubscription( + subscription: Subscription, + cb: (message: EBMessage) => Promise | undefined>, + ): Promise { + if (this.isStarted) { + throw new UnhandledError( + StatusCode.InternalServerError, + 'subscriptions must be registered before starting the http event bridge', + ) + } + + const fn = getSubscriptionHandler.bind(this) + const handler = fn(subscription, cb, this.config.subscriptionPayloadAsCloudEvent) + + const path = this.client.getInternalPathForSubscription(subscription.subscriber) + + this.app.post(path, handler) + this.logger.debug({ path }, 'subscription added') + return path + } + + async unregisterSubscription(address: EBMessageAddress): Promise { + this.logger.debug({ address }, 'unregister subscription') + } + + async isReady(): Promise { + return this.isStarted && !this.isShuttingDown + } + + async isHealthy(): Promise { + if (!this.isStarted) { + return false + } + return this.client.isSidecarAvailable() + } + + /** + * Shut down event bridge as gracefully as possible + */ + async destroy(): Promise { + if (!this.server) { + return + } + if (this.server instanceof Server) { + this.server.closeIdleConnections() + } + const server = this.server + await new Promise(resolve => server.close(resolve)) + } } diff --git a/packages/base-http-bridge/src/HttpEventBridge/getCommandHandler.impl.ts b/packages/base-http-bridge/src/HttpEventBridge/getCommandHandler.impl.ts index 13b126c53..b4093e24a 100644 --- a/packages/base-http-bridge/src/HttpEventBridge/getCommandHandler.impl.ts +++ b/packages/base-http-bridge/src/HttpEventBridge/getCommandHandler.impl.ts @@ -1,23 +1,26 @@ // file deepcode ignore ServerLeak: -import { context, propagation, SpanKind } from '@opentelemetry/api' -import { SemanticAttributes } from '@opentelemetry/semantic-conventions' +import { SpanKind, context, propagation } from '@opentelemetry/api' +import { ATTR_URL_FULL } from '@opentelemetry/semantic-conventions' + +import { ATTR_HTTP_HOST, ATTR_HTTP_METHOD, ATTR_HTTP_STATUS_CODE } from '@opentelemetry/semantic-conventions/incubating' + import type { - Command, - CommandErrorResponse, - CommandSuccessResponse, - DefinitionEventBridgeConfig, - EBMessageAddress, - HttpExposedServiceMeta, + Command, + CommandErrorResponse, + CommandSuccessResponse, + DefinitionEventBridgeConfig, + EBMessageAddress, + HttpExposedServiceMeta, } from '@purista/core' import { - getTimeoutPromise, - HandledError, - PuristaSpanName, - serializeOtp, - StatusCode, - throwIfNotValidMessage, - UnhandledError, + HandledError, + PuristaSpanName, + StatusCode, + UnhandledError, + getTimeoutPromise, + serializeOtp, + throwIfNotValidMessage, } from '@purista/core' import { HTTP } from 'cloudevents' @@ -25,95 +28,96 @@ import type { HttpEventBridge } from './HttpEventBridge.impl.js' import type { HttpEventBridgeConfig, RouterFunction } from './types/index.js' export const getCommandHandler = function ( - this: HttpEventBridge, - address: EBMessageAddress, - cb: ( - message: Command, - ) => Promise< - Readonly> | Readonly> - >, - _metadata: HttpExposedServiceMeta, - _eventBridgeConfig: DefinitionEventBridgeConfig, - wrappedInCloudEvent = false, + this: HttpEventBridge, + address: EBMessageAddress, + cb: ( + message: Command, + ) => Promise< + Readonly> | Readonly> + >, + _metadata: HttpExposedServiceMeta, + _eventBridgeConfig: DefinitionEventBridgeConfig, + wrappedInCloudEvent = false, ): RouterFunction { - const handler: RouterFunction = async (c) => { - const parentContext = propagation.extract(context.active(), c.req.raw.headers) - - this.logger.info({ headers: c.req.raw.headers }, 'command handler headers') - - return await this.startActiveSpan( - PuristaSpanName.EventBridgeCommandReceived, - { kind: SpanKind.CONSUMER }, - parentContext, - async (span) => { - const hostname = process.env.HOSTNAME ?? 'unknown' - span.setAttribute(SemanticAttributes.HTTP_URL, c.req.url || '') - span.setAttribute(SemanticAttributes.HTTP_METHOD, c.req.method || '') - span.setAttribute(SemanticAttributes.HTTP_HOST, hostname) - - try { - if (c.req.method !== 'POST') { - throw new UnhandledError(StatusCode.MethodNotAllowed, 'Unsupported method ' + c.req.method) - } - - let message: Command - - if (wrappedInCloudEvent) { - const body = await c.req.text() - const headers = [...c.req.raw.headers.entries()].reduce((prev: Record, val) => { - return { ...prev, [val[0]]: val[1] } - }, {}) - - const event = HTTP.toEvent({ headers, body }) - if (Array.isArray(event)) { - throw new UnhandledError( - StatusCode.NotImplemented, - 'Support of multiple events per command call is not supported', - ) - } - message = event.data as Command - } else { - try { - message = await c.req.json() - } catch (error) { - throw new HandledError(StatusCode.BadRequest, 'payload must be a parsable json') - } - } - - throwIfNotValidMessage(message) - - message.otp = serializeOtp() - - const msg = await getTimeoutPromise(cb(message), this.config.defaultCommandTimeout) - - if (msg.eventName) { - await this.emitMessage(msg) - } - - // empty response - if (msg.payload === undefined || msg.payload === '') { - const status = StatusCode.NoContent - - span.setAttribute(SemanticAttributes.HTTP_STATUS_CODE, status) - - c.status(status) - return c.body(null) - } - - const payload = typeof msg.payload === 'string' ? msg.payload : JSON.stringify(msg) - - const status = StatusCode.OK - return c.json(payload, status as any) - } catch (error) { - const err = error instanceof UnhandledError ? error : UnhandledError.fromError(error) - span.recordException(err) - this.logger.error({ err }, err.message) - - return c.json(err.getErrorResponse(), err.errorCode as any) - } - }, - ) - } - - return handler + const handler: RouterFunction = async c => { + const parentContext = propagation.extract(context.active(), c.req.raw.headers) + + this.logger.info({ headers: c.req.raw.headers }, 'command handler headers') + + return await this.startActiveSpan( + PuristaSpanName.EventBridgeCommandReceived, + { kind: SpanKind.CONSUMER }, + parentContext, + async span => { + const hostname = process.env.HOSTNAME ?? 'unknown' + span.setAttribute(ATTR_URL_FULL, c.req.url || '') + span.setAttribute(ATTR_HTTP_METHOD, c.req.method || '') + span.setAttribute(ATTR_HTTP_HOST, hostname) + + try { + if (c.req.method !== 'POST') { + throw new UnhandledError(StatusCode.MethodNotAllowed, `Unsupported method ${c.req.method}`) + } + + let message: Command + + if (wrappedInCloudEvent) { + const body = await c.req.text() + const headers = [...c.req.raw.headers.entries()].reduce((prev: Record, val) => { + // biome-ignore lint/performance/noAccumulatingSpread: + return { ...prev, [val[0]]: val[1] } + }, {}) + + const event = HTTP.toEvent({ headers, body }) + if (Array.isArray(event)) { + throw new UnhandledError( + StatusCode.NotImplemented, + 'Support of multiple events per command call is not supported', + ) + } + message = event.data as Command + } else { + try { + message = await c.req.json() + } catch (error) { + throw new HandledError(StatusCode.BadRequest, 'payload must be a parsable json') + } + } + + throwIfNotValidMessage(message) + + message.otp = serializeOtp() + + const msg = await getTimeoutPromise(cb(message), this.config.defaultCommandTimeout) + + if (msg.eventName) { + await this.emitMessage(msg) + } + + // empty response + if (msg.payload === undefined || msg.payload === '') { + const status = StatusCode.NoContent + + span.setAttribute(ATTR_HTTP_STATUS_CODE, status) + + c.status(status) + return c.body(null) + } + + const payload = typeof msg.payload === 'string' ? msg.payload : JSON.stringify(msg) + + const status = StatusCode.OK + return c.json(payload, status as any) + } catch (error) { + const err = error instanceof UnhandledError ? error : UnhandledError.fromError(error) + span.recordException(err) + this.logger.error({ err }, err.message) + + return c.json(err.getErrorResponse(), err.errorCode as any) + } + }, + ) + } + + return handler } diff --git a/packages/base-http-bridge/src/HttpEventBridge/getCommandHandlerRestApi.impl.ts b/packages/base-http-bridge/src/HttpEventBridge/getCommandHandlerRestApi.impl.ts index 24b945570..c1dc471cc 100644 --- a/packages/base-http-bridge/src/HttpEventBridge/getCommandHandlerRestApi.impl.ts +++ b/packages/base-http-bridge/src/HttpEventBridge/getCommandHandlerRestApi.impl.ts @@ -1,182 +1,185 @@ import type { ParsedUrlQuery } from 'node:querystring' import { parse } from 'node:querystring' -import { context, propagation, SpanKind, SpanStatusCode } from '@opentelemetry/api' -import { SemanticAttributes } from '@opentelemetry/semantic-conventions' +import { SpanKind, SpanStatusCode, context, propagation } from '@opentelemetry/api' +import { ATTR_URL_FULL } from '@opentelemetry/semantic-conventions' + +import { ATTR_HTTP_HOST, ATTR_HTTP_METHOD, ATTR_HTTP_STATUS_CODE } from '@opentelemetry/semantic-conventions/incubating' + import type { - Command, - CommandErrorResponse, - CommandSuccessResponse, - DefinitionEventBridgeConfig, - EBMessageAddress, - HttpExposedServiceMeta, + Command, + CommandErrorResponse, + CommandSuccessResponse, + DefinitionEventBridgeConfig, + EBMessageAddress, + HttpExposedServiceMeta, } from '@purista/core' import { - EBMessageType, - getErrorMessageForCode, - getTimeoutPromise, - HandledError, - isCommandErrorResponse, - PuristaSpanName, - serializeOtp, - StatusCode, - UnhandledError, + EBMessageType, + HandledError, + PuristaSpanName, + StatusCode, + UnhandledError, + getErrorMessageForCode, + getTimeoutPromise, + isCommandErrorResponse, + serializeOtp, } from '@purista/core' -import type { StatusCode as HonoStatusCode } from 'hono/utils/http-status' +import type { ContentfulStatusCode } from 'hono/utils/http-status' import type { HttpEventBridge } from './HttpEventBridge.impl.js' import type { HttpEventBridgeConfig, RouterFunction } from './types/index.js' export const getCommandHandlerRestApi = function ( - this: HttpEventBridge, - address: EBMessageAddress, - cb: ( - message: Command, - ) => Promise< - Readonly> | Readonly> - >, - metadata: HttpExposedServiceMeta, - _eventBridgeConfig: DefinitionEventBridgeConfig, + this: HttpEventBridge, + address: EBMessageAddress, + cb: ( + message: Command, + ) => Promise< + Readonly> | Readonly> + >, + metadata: HttpExposedServiceMeta, + _eventBridgeConfig: DefinitionEventBridgeConfig, ): RouterFunction { - const handler: RouterFunction = async (c) => { - const parentContext = propagation.extract(context.active(), c.req.raw.headers) - - return await this.startActiveSpan( - PuristaSpanName.KubernetesHttpRequest, - { kind: SpanKind.CONSUMER }, - parentContext, - async (span) => { - const hostname = process.env.HOSTNAME ?? 'unknown' - span.setAttribute(SemanticAttributes.HTTP_URL, c.req.url || '') - span.setAttribute(SemanticAttributes.HTTP_METHOD, c.req.method || '') - span.setAttribute(SemanticAttributes.HTTP_HOST, hostname) - - try { - const queryParams: ParsedUrlQuery = {} - - // allow only defined parameters - if (metadata.expose.http.openApi?.query) { - const parsedQueries = parse(c.req.url || '') - metadata.expose.http.openApi.query.forEach((qp) => { - queryParams[qp.name] = parsedQueries[qp.name] - if (qp.required && !parsedQueries[qp.name]) { - throw new HandledError(StatusCode.BadRequest, `query parameter ${qp.name} is required`) - } - }) - } - - let body: unknown - if (c.req.method === 'POST' || c.req.method === 'PUT' || c.req.method === 'PATCH') { - const contentType = metadata.expose.contentTypeRequest ?? 'application/json' - - body = contentType.toLowerCase() === 'application/json' ? await c.req.json() : await c.req.text() - } - - const parameter = c.req.param - - const command: Command = { - id: '', - messageType: EBMessageType.Command, - correlationId: '', - timestamp: Date.now(), - contentType: metadata.expose.contentTypeResponse ?? 'application/json', - contentEncoding: metadata.expose.contentEncodingResponse ?? 'utf-8', - otp: serializeOtp(), - receiver: { - ...address, - }, - sender: { - serviceName: '', - serviceVersion: '', - serviceTarget: '', - instanceId: '', - }, - payload: { - payload: body, - parameter: { - ...queryParams, - ...parameter, - }, - }, - } - - const result = await getTimeoutPromise(cb(command), this.config.defaultCommandTimeout) - - if (isCommandErrorResponse(result)) { - const status = result.payload.status - - span.setAttribute(SemanticAttributes.HTTP_STATUS_CODE, status) - - span.setStatus({ - code: SpanStatusCode.ERROR, - message: result.payload.message, - }) - - span.end() - return c.json(result.payload, status as any) - } - - if (result.eventName) { - await this.emitMessage(result) - } - - // empty response - if (result.payload === undefined || result.payload === '') { - const status = StatusCode.NoContent - span.setAttribute(SemanticAttributes.HTTP_STATUS_CODE, status) - - span.end() - return new Response(undefined, { - status, - statusText: getErrorMessageForCode(status), - headers: { - 'content-type': `${metadata.expose.contentTypeResponse} || 'application/json'; charset=${ - metadata.expose.contentEncodingResponse ?? 'utf-8' - }`, - }, - }) - } - - span.setAttribute(SemanticAttributes.HTTP_STATUS_CODE, StatusCode.OK) - - let payload = '' - if (typeof result.payload === 'string') { - payload = result.payload - } else { - payload = JSON.stringify(result.payload) - } - - const status = StatusCode.OK - - span.end() - return new Response(JSON.stringify(payload), { - status, - statusText: getErrorMessageForCode(status), - headers: { - 'content-type': `${metadata.expose.contentTypeResponse} || 'application/json'; charset=${ - metadata.expose.contentEncodingResponse ?? 'utf-8' - }`, - }, - }) - } catch (error) { - const err = - error instanceof HandledError || error instanceof UnhandledError ? error : UnhandledError.fromError(error) - - this.logger.error({ err }, err.message) - span.setStatus({ - code: SpanStatusCode.ERROR, - message: err.message, - }) - - span.recordException(err) - const status = err.errorCode - span.end() - - return c.json(err.getErrorResponse(), status as HonoStatusCode) - } - }, - ) - } - - return handler + const handler: RouterFunction = async c => { + const parentContext = propagation.extract(context.active(), c.req.raw.headers) + + return await this.startActiveSpan( + PuristaSpanName.KubernetesHttpRequest, + { kind: SpanKind.CONSUMER }, + parentContext, + async span => { + const hostname = process.env.HOSTNAME ?? 'unknown' + span.setAttribute(ATTR_URL_FULL, c.req.url || '') + span.setAttribute(ATTR_HTTP_METHOD, c.req.method || '') + span.setAttribute(ATTR_HTTP_HOST, hostname) + + try { + const queryParams: ParsedUrlQuery = {} + + // allow only defined parameters + if (metadata.expose.http.openApi?.query) { + const parsedQueries = parse(c.req.url || '') + for (const qp of metadata.expose.http.openApi.query) { + queryParams[qp.name] = parsedQueries[qp.name] + if (qp.required && !parsedQueries[qp.name]) { + throw new HandledError(StatusCode.BadRequest, `query parameter ${qp.name} is required`) + } + } + } + + let body: unknown + if (c.req.method === 'POST' || c.req.method === 'PUT' || c.req.method === 'PATCH') { + const contentType = metadata.expose.contentTypeRequest ?? 'application/json' + + body = contentType.toLowerCase() === 'application/json' ? await c.req.json() : await c.req.text() + } + + const parameter = c.req.param + + const command: Command = { + id: '', + messageType: EBMessageType.Command, + correlationId: '', + timestamp: Date.now(), + contentType: metadata.expose.contentTypeResponse ?? 'application/json', + contentEncoding: metadata.expose.contentEncodingResponse ?? 'utf-8', + otp: serializeOtp(), + receiver: { + ...address, + }, + sender: { + serviceName: '', + serviceVersion: '', + serviceTarget: '', + instanceId: '', + }, + payload: { + payload: body, + parameter: { + ...queryParams, + ...parameter, + }, + }, + } + + const result = await getTimeoutPromise(cb(command), this.config.defaultCommandTimeout) + + if (isCommandErrorResponse(result)) { + const status = result.payload.status + + span.setAttribute(ATTR_HTTP_STATUS_CODE, status) + + span.setStatus({ + code: SpanStatusCode.ERROR, + message: result.payload.message, + }) + + span.end() + return c.json(result.payload, status as any) + } + + if (result.eventName) { + await this.emitMessage(result) + } + + // empty response + if (result.payload === undefined || result.payload === '') { + const status = StatusCode.NoContent + span.setAttribute(ATTR_HTTP_STATUS_CODE, status) + + span.end() + return new Response(undefined, { + status, + statusText: getErrorMessageForCode(status), + headers: { + 'content-type': `${metadata.expose.contentTypeResponse} || 'application/json'; charset=${ + metadata.expose.contentEncodingResponse ?? 'utf-8' + }`, + }, + }) + } + + span.setAttribute(ATTR_HTTP_STATUS_CODE, StatusCode.OK) + + let payload = '' + if (typeof result.payload === 'string') { + payload = result.payload + } else { + payload = JSON.stringify(result.payload) + } + + const status = StatusCode.OK + + span.end() + return new Response(JSON.stringify(payload), { + status, + statusText: getErrorMessageForCode(status), + headers: { + 'content-type': `${metadata.expose.contentTypeResponse} || 'application/json'; charset=${ + metadata.expose.contentEncodingResponse ?? 'utf-8' + }`, + }, + }) + } catch (error) { + const err = + error instanceof HandledError || error instanceof UnhandledError ? error : UnhandledError.fromError(error) + + this.logger.error({ err }, err.message) + span.setStatus({ + code: SpanStatusCode.ERROR, + message: err.message, + }) + + span.recordException(err) + const status = err.errorCode + span.end() + + return c.json(err.getErrorResponse(), status as ContentfulStatusCode) + } + }, + ) + } + + return handler } diff --git a/packages/base-http-bridge/src/HttpEventBridge/getDefaultHttpEventBridgeConfig.impl.ts b/packages/base-http-bridge/src/HttpEventBridge/getDefaultHttpEventBridgeConfig.impl.ts index dd77dbd94..784906133 100644 --- a/packages/base-http-bridge/src/HttpEventBridge/getDefaultHttpEventBridgeConfig.impl.ts +++ b/packages/base-http-bridge/src/HttpEventBridge/getDefaultHttpEventBridgeConfig.impl.ts @@ -3,17 +3,17 @@ import type { Complete, EventBridgeConfig } from '@purista/core' import type { HttpEventBridgeConfig } from './types/index.js' export const getDefaultHttpEventBridgeConfig = (): EventBridgeConfig> => { - const config: Complete> = { - name: 'HttpEventBridge', - serverHost: '127.0.0.1', - serverPort: 8080, - apiPrefix: '/api', - enableRestApiExpose: true, - pathPrefix: 'purista', - subscriptionPayloadAsCloudEvent: false, - commandPayloadAsCloudEvent: false, - enableHttpCompression: true, - } + const config: Complete> = { + name: 'HttpEventBridge', + serverHost: '127.0.0.1', + serverPort: 8080, + apiPrefix: '/api', + enableRestApiExpose: true, + pathPrefix: 'purista', + subscriptionPayloadAsCloudEvent: false, + commandPayloadAsCloudEvent: false, + enableHttpCompression: true, + } - return config + return config } diff --git a/packages/base-http-bridge/src/HttpEventBridge/getSubscriptionHandler.impl.ts b/packages/base-http-bridge/src/HttpEventBridge/getSubscriptionHandler.impl.ts index 3d7c7a883..e6ef3d43c 100644 --- a/packages/base-http-bridge/src/HttpEventBridge/getSubscriptionHandler.impl.ts +++ b/packages/base-http-bridge/src/HttpEventBridge/getSubscriptionHandler.impl.ts @@ -1,94 +1,98 @@ // file deepcode ignore ServerLeak: -import { context, propagation, SpanKind } from '@opentelemetry/api' -import { SemanticAttributes } from '@opentelemetry/semantic-conventions' +import { SpanKind, context, propagation } from '@opentelemetry/api' +import { ATTR_URL_FULL } from '@opentelemetry/semantic-conventions' + +import { ATTR_HTTP_HOST, ATTR_HTTP_METHOD, ATTR_HTTP_STATUS_CODE } from '@opentelemetry/semantic-conventions/incubating' + import type { CustomMessage, EBMessage, Subscription } from '@purista/core' import { - getTimeoutPromise, - HandledError, - PuristaSpanName, - serializeOtp, - StatusCode, - throwIfNotValidMessage, - UnhandledError, + HandledError, + PuristaSpanName, + StatusCode, + UnhandledError, + getTimeoutPromise, + serializeOtp, + throwIfNotValidMessage, } from '@purista/core' import { HTTP } from 'cloudevents' -import type { StatusCode as HonoStatusCode } from 'hono/utils/http-status' +import type { ContentfulStatusCode } from 'hono/utils/http-status' import type { HttpEventBridge } from './HttpEventBridge.impl.js' import type { HttpEventBridgeConfig, RouterFunction } from './types/index.js' export const getSubscriptionHandler = function ( - this: HttpEventBridge, - subscription: Subscription, - cb: (message: EBMessage) => Promise | undefined>, - wrappedInCloudEvent = false, + this: HttpEventBridge, + subscription: Subscription, + cb: (message: EBMessage) => Promise | undefined>, + wrappedInCloudEvent = false, ): RouterFunction { - const handler: RouterFunction = async (c) => { - const parentContext = propagation.extract(context.active(), c.req.raw.headers) - - return await this.startActiveSpan( - PuristaSpanName.EventBridgeCommandReceived, - { kind: SpanKind.CONSUMER }, - parentContext, - async (span) => { - const hostname = process.env.HOSTNAME ?? 'unknown' - span.setAttribute(SemanticAttributes.HTTP_URL, c.req.url || '') - span.setAttribute(SemanticAttributes.HTTP_METHOD, c.req.method || '') - span.setAttribute(SemanticAttributes.HTTP_HOST, hostname) - - try { - if (c.req.method !== 'POST') { - throw new UnhandledError(StatusCode.MethodNotAllowed, 'Unsupported method ' + c.req.method) - } - - const headers = [...c.req.raw.headers.entries()].reduce((prev: Record, val) => { - return { ...prev, [val[0]]: val[1] } - }, {}) - - let message: EBMessage - - if (wrappedInCloudEvent) { - const body = await c.req.text() - - const event = HTTP.toEvent({ headers, body }) - if (Array.isArray(event)) { - throw new UnhandledError( - StatusCode.NotImplemented, - 'Support of multiple events per subscription call is not supported', - ) - } - message = event.data as EBMessage - } else { - try { - message = await c.req.json() - } catch (error) { - throw new HandledError(StatusCode.BadRequest, 'payload must be a parsable json') - } - } - - throwIfNotValidMessage(message) - - message.otp = serializeOtp() - - span.setAttribute(SemanticAttributes.HTTP_STATUS_CODE, StatusCode.NoContent) - - const msg = await getTimeoutPromise(cb(message), this.config.defaultCommandTimeout) - - if (msg) { - await this.emitMessage(msg) - } - - return c.json(undefined, StatusCode.NoContent) - } catch (error) { - const err = error instanceof UnhandledError ? error : UnhandledError.fromError(error) - span.recordException(err) - this.logger.error({ err }, err.message) - return c.json(err.getErrorResponse(), err.errorCode as HonoStatusCode) - } - }, - ) - } - - return handler + const handler: RouterFunction = async c => { + const parentContext = propagation.extract(context.active(), c.req.raw.headers) + + return await this.startActiveSpan( + PuristaSpanName.EventBridgeCommandReceived, + { kind: SpanKind.CONSUMER }, + parentContext, + async span => { + const hostname = process.env.HOSTNAME ?? 'unknown' + span.setAttribute(ATTR_URL_FULL, c.req.url || '') + span.setAttribute(ATTR_HTTP_METHOD, c.req.method || '') + span.setAttribute(ATTR_HTTP_HOST, hostname) + + try { + if (c.req.method !== 'POST') { + throw new UnhandledError(StatusCode.MethodNotAllowed, `Unsupported method ${c.req.method}`) + } + + const headers = [...c.req.raw.headers.entries()].reduce((prev: Record, val) => { + // biome-ignore lint/performance/noAccumulatingSpread: + return { ...prev, [val[0]]: val[1] } + }, {}) + + let message: EBMessage + + if (wrappedInCloudEvent) { + const body = await c.req.text() + + const event = HTTP.toEvent({ headers, body }) + if (Array.isArray(event)) { + throw new UnhandledError( + StatusCode.NotImplemented, + 'Support of multiple events per subscription call is not supported', + ) + } + message = event.data as EBMessage + } else { + try { + message = await c.req.json() + } catch (error) { + throw new HandledError(StatusCode.BadRequest, 'payload must be a parsable json') + } + } + + throwIfNotValidMessage(message) + + message.otp = serializeOtp() + + span.setAttribute(ATTR_HTTP_STATUS_CODE, StatusCode.NoContent) + + const msg = await getTimeoutPromise(cb(message), this.config.defaultCommandTimeout) + + if (msg) { + await this.emitMessage(msg) + } + + return c.body(null, StatusCode.NoContent) + } catch (error) { + const err = error instanceof UnhandledError ? error : UnhandledError.fromError(error) + span.recordException(err) + this.logger.error({ err }, err.message) + return c.json(err.getErrorResponse(), err.errorCode as ContentfulStatusCode) + } + }, + ) + } + + return handler } diff --git a/packages/base-http-bridge/src/HttpEventBridge/healthzRoute.impl.ts b/packages/base-http-bridge/src/HttpEventBridge/healthzRoute.impl.ts index ce7bbcf64..0aae7ec46 100644 --- a/packages/base-http-bridge/src/HttpEventBridge/healthzRoute.impl.ts +++ b/packages/base-http-bridge/src/HttpEventBridge/healthzRoute.impl.ts @@ -3,10 +3,10 @@ import { StatusCode } from '@purista/core' import type { RouterFunction } from './types/index.js' export const healthzRoute: RouterFunction = async function (c) { - const isHealthy = await this.isHealthy() - if (isHealthy) { - return c.json({ message: 'ok', status: 200 }) - } - this.logger.error('health not ok') - return c.json({ status: StatusCode.InternalServerError, message: 'not ok' }, StatusCode.InternalServerError) + const isHealthy = await this.isHealthy() + if (isHealthy) { + return c.json({ message: 'ok', status: 200 }) + } + this.logger.error('health not ok') + return c.json({ status: StatusCode.InternalServerError, message: 'not ok' }, StatusCode.InternalServerError) } diff --git a/packages/base-http-bridge/src/HttpEventBridge/types/HttpEventBridgeClient.ts b/packages/base-http-bridge/src/HttpEventBridge/types/HttpEventBridgeClient.ts index 10fd7d0c3..813b6f8bb 100644 --- a/packages/base-http-bridge/src/HttpEventBridge/types/HttpEventBridgeClient.ts +++ b/packages/base-http-bridge/src/HttpEventBridge/types/HttpEventBridgeClient.ts @@ -5,51 +5,51 @@ import type { Command, CommandResponse, EBMessage, EBMessageAddress, HttpExposed * This client is responsible for the communication to the sidecar service. */ export interface HttpEventBridgeClient { - /** - * Generate the url path of the command. - * This url is a POST endpoint and expects a command message as payload - * @param address - * @returns url path of endpoint - */ - getInternalPathForCommand: (address: EBMessageAddress) => string + /** + * Generate the url path of the command. + * This url is a POST endpoint and expects a command message as payload + * @param address + * @returns url path of endpoint + */ + getInternalPathForCommand: (address: EBMessageAddress) => string - /** - * Generate the url path of the command based on the command builder `exposeAsHttpEndpoint` settings. - * This url is a POST endpoint and expects the payload and parameter as defined for exposing. - * @param address - * @returns url path of endpoint - */ - getApiPathForCommand: (addess: EBMessageAddress, metadata: HttpExposedServiceMeta) => string + /** + * Generate the url path of the command based on the command builder `exposeAsHttpEndpoint` settings. + * This url is a POST endpoint and expects the payload and parameter as defined for exposing. + * @param address + * @returns url path of endpoint + */ + getApiPathForCommand: (addess: EBMessageAddress, metadata: HttpExposedServiceMeta) => string - /** - * Generate the url path of the subscription. - * This url is a POST endpoint. - * The expected payload is a EBMessage or an CloudEvent with an EBMessage as data depending on config settings - * @param address - * @returns url path of endpoint - */ - getInternalPathForSubscription: (address: EBMessageAddress) => string + /** + * Generate the url path of the subscription. + * This url is a POST endpoint. + * The expected payload is a EBMessage or an CloudEvent with an EBMessage as data depending on config settings + * @param address + * @returns url path of endpoint + */ + getInternalPathForSubscription: (address: EBMessageAddress) => string - /** - * Invoke a command - * @param command Command - * @param headers optional HTTP header - * @param timeout the command timeout - * @returns - */ - invoke: (command: Command, headers?: Record, timeout?: number) => Promise + /** + * Invoke a command + * @param command Command + * @param headers optional HTTP header + * @param timeout the command timeout + * @returns + */ + invoke: (command: Command, headers?: Record, timeout?: number) => Promise - /** - * Send a EBMessage as event to the underlaying message infrastructure. - * @param message - * @param headers - * @returns - */ - sendEvent: (message: EBMessage, headers?: Record) => Promise + /** + * Send a EBMessage as event to the underlaying message infrastructure. + * @param message + * @param headers + * @returns + */ + sendEvent: (message: EBMessage, headers?: Record) => Promise - /** - * Checks if the sidecar container is available to be able to send events and invoke commands - * @returns boolean - */ - isSidecarAvailable: () => Promise + /** + * Checks if the sidecar container is available to be able to send events and invoke commands + * @returns boolean + */ + isSidecarAvailable: () => Promise } diff --git a/packages/base-http-bridge/src/HttpEventBridge/types/HttpEventBridgeConfig.ts b/packages/base-http-bridge/src/HttpEventBridge/types/HttpEventBridgeConfig.ts index abf8203de..84f016867 100644 --- a/packages/base-http-bridge/src/HttpEventBridge/types/HttpEventBridgeConfig.ts +++ b/packages/base-http-bridge/src/HttpEventBridge/types/HttpEventBridgeConfig.ts @@ -2,84 +2,84 @@ import type { Server } from 'node:http' import type { Http2SecureServer, Http2Server } from 'node:http2' export type HttpEventBridgeConfig = { - /** - * name of the bridge - * - * @default HttpEventBridge - * */ - name?: string + /** + * name of the bridge + * + * @default HttpEventBridge + * */ + name?: string - /** - * The serve function is depending on the runtime. - * - * - Bun: `Bun.serve` - * - Node.js: `serve` function from additional package `@hono/hono-node-server` - * - Deno: `serve` function from package `https://deno.land/std/http/server.ts` - * - * @see https://hono.dev - */ - serve: (options: { - fetch: (request: Request) => Promise | unknown - port?: number - hostname?: string - }) => Server | Http2Server | Http2SecureServer + /** + * The serve function is depending on the runtime. + * + * - Bun: `Bun.serve` + * - Node.js: `serve` function from additional package `@hono/hono-node-server` + * - Deno: `serve` function from package `https://deno.land/std/http/server.ts` + * + * @see https://hono.dev + */ + serve: (options: { + fetch: (request: Request) => Promise | unknown + port?: number + hostname?: string + }) => Server | Http2Server | Http2SecureServer - /** - * Host of the server. - * - * @default 127.0.0.1 - */ - serverHost?: string + /** + * Host of the server. + * + * @default 127.0.0.1 + */ + serverHost?: string - /** - * Port of the server. - * - * @default 8080 - */ - serverPort?: number + /** + * Port of the server. + * + * @default 8080 + */ + serverPort?: number - /** - * the prefix to be used for exposing commands as endpoints expecting a event bus message - * - * @default purista - */ - pathPrefix?: string + /** + * the prefix to be used for exposing commands as endpoints expecting a event bus message + * + * @default purista + */ + pathPrefix?: string - /** - * the prefix to be used if the command is configured as REST api endpoint according to the OpenAPI defintion - * needs to `enableRestApiExpose` set to `true` - * - * @default /api - */ - apiPrefix?: string + /** + * the prefix to be used if the command is configured as REST api endpoint according to the OpenAPI defintion + * needs to `enableRestApiExpose` set to `true` + * + * @default /api + */ + apiPrefix?: string - /** - * expose commands as regular REST endpoints when they are configured as endpoints - * - * @default true - */ - enableRestApiExpose?: boolean + /** + * expose commands as regular REST endpoints when they are configured as endpoints + * + * @default true + */ + enableRestApiExpose?: boolean - /** - * subscription invocations are wrapped in CloudEvent - * - * @link https://github.com/cloudevents/spec/tree/v1.0 - * - * @default false - */ - subscriptionPayloadAsCloudEvent?: boolean + /** + * subscription invocations are wrapped in CloudEvent + * + * @link https://github.com/cloudevents/spec/tree/v1.0 + * + * @default false + */ + subscriptionPayloadAsCloudEvent?: boolean - /** - * command invocations are wrapped in CloudEvent - * - * @link https://github.com/cloudevents/spec/tree/v1.0 - * - * @default false - */ - commandPayloadAsCloudEvent?: boolean - /** - * enable HTTP compression in web server - * @default true - */ - enableHttpCompression?: boolean + /** + * command invocations are wrapped in CloudEvent + * + * @link https://github.com/cloudevents/spec/tree/v1.0 + * + * @default false + */ + commandPayloadAsCloudEvent?: boolean + /** + * enable HTTP compression in web server + * @default true + */ + enableHttpCompression?: boolean } diff --git a/packages/base-http-bridge/src/HttpEventBridge/types/RouterFunction.ts b/packages/base-http-bridge/src/HttpEventBridge/types/RouterFunction.ts index a5a894f33..76417b998 100644 --- a/packages/base-http-bridge/src/HttpEventBridge/types/RouterFunction.ts +++ b/packages/base-http-bridge/src/HttpEventBridge/types/RouterFunction.ts @@ -4,4 +4,4 @@ import type { HttpEventBridge } from '../HttpEventBridge.impl.js' import type { HttpEventBridgeConfig } from './HttpEventBridgeConfig.js' export type RouterFunction = HttpEventBridge> = - (this: T, c: Context) => Promise + (this: T, c: Context) => Promise diff --git a/packages/base-http-bridge/src/index.test.ts b/packages/base-http-bridge/src/index.test.ts index d3baabdd0..4c64b48c2 100644 --- a/packages/base-http-bridge/src/index.test.ts +++ b/packages/base-http-bridge/src/index.test.ts @@ -1,11 +1,11 @@ import { HttpEventBridge, puristaVersion } from './index.js' describe('exports HttpEventBridge', () => { - it('has a version', () => { - expect(puristaVersion).toBeDefined() - }) - it('exports HttpEventBridge', () => { - // http event bridge - expect(HttpEventBridge).toBeDefined() - }) + it('has a version', () => { + expect(puristaVersion).toBeDefined() + }) + it('exports HttpEventBridge', () => { + // http event bridge + expect(HttpEventBridge).toBeDefined() + }) }) diff --git a/packages/base-http-bridge/tsconfig.json b/packages/base-http-bridge/tsconfig.json index cdb332b03..7b117ebaf 100644 --- a/packages/base-http-bridge/tsconfig.json +++ b/packages/base-http-bridge/tsconfig.json @@ -1,20 +1,12 @@ { - "extends": "../../tsconfig.json", - "compilerOptions": { - "outDir": "./dist", - "declaration": true, - "sourceMap": false, - "declarationMap": true, - "types": [ - "vitest/globals", - "node" - ] - }, - "include": [ - "./src/**/*", - "./test/*", - ], - "exclude": [ - "./**/*.d.ts" - ] -} \ No newline at end of file + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "./dist", + "declaration": true, + "sourceMap": false, + "declarationMap": true, + "types": ["vitest/globals", "node"] + }, + "include": ["./src/**/*", "./test/*"], + "exclude": ["./**/*.d.ts"] +} diff --git a/packages/base-http-bridge/typedoc.json b/packages/base-http-bridge/typedoc.json index 355bf0f98..71c4b2283 100644 --- a/packages/base-http-bridge/typedoc.json +++ b/packages/base-http-bridge/typedoc.json @@ -1,6 +1,5 @@ { - - "extends": ["../../typedoc.base.json"], - "entryPoints": ["src/index.ts"], - "tsconfig": "./tsconfig.json" -} \ No newline at end of file + "extends": ["../../typedoc.base.json"], + "entryPoints": ["src/index.ts"], + "tsconfig": "./tsconfig.json" +} diff --git a/packages/cli/blueprint/public/index.html.hbs b/packages/cli/blueprint/public/index.html.hbs index 798a0cf0a..214cbd8d8 100644 --- a/packages/cli/blueprint/public/index.html.hbs +++ b/packages/cli/blueprint/public/index.html.hbs @@ -25,7 +25,7 @@ You can use the PURISTA cli to quickly add services, commands and subscription
You can run the cli with purista add
- Optionally, you can directly tell, what kind of ressource you like to add:
+ Optionally, you can directly tell, what kind of resource you like to add:
Add a new service purista add service
Add a command to a existing service purista add command
Add a subscription to a existing service purista add subscription
diff --git a/packages/cli/blueprint/src/service/serviceName/v1/command/name/name.test.ts.hbs b/packages/cli/blueprint/src/service/serviceName/v1/command/name/name.test.ts.hbs index cd9025ee2..ae46fb580 100644 --- a/packages/cli/blueprint/src/service/serviceName/v1/command/name/name.test.ts.hbs +++ b/packages/cli/blueprint/src/service/serviceName/v1/command/name/name.test.ts.hbs @@ -6,9 +6,9 @@ import { {{camelCase name}}CommandBuilder } from './{{camelCase name}}CommandBui import { {{properCase service.name}}V{{service.version}}{{properCase name}}InputPayload, {{properCase service.name}}V{{service.version}}{{properCase name}}InputParameter } from './types{{fileExt}}' describe('service {{properCase service.name}} version {{service.version}} - command {{camelCase name}}', () => { - let sandbox = createSandbox() + const sandbox = createSandbox() beforeEach(() => { - sandbox = createSandbox() + // add your before each here }) afterEach(() => { @@ -16,7 +16,7 @@ describe('service {{properCase service.name}} version {{service.version}} - comm }) test('does not throw', async () => { - const service = {{camelCase service.name}}V{{service.version}}Service.getInstance(getEventBridgeMock(sandbox).mock, { logger: getLoggerMock(sandbox).mock }) + const service = await {{camelCase service.name}}V{{service.version}}Service.getInstance(getEventBridgeMock(sandbox).mock, { logger: getLoggerMock(sandbox).mock }) const {{camelCase name}} = safeBind({{camelCase name}}CommandBuilder.getCommandFunction(), service) @@ -24,7 +24,7 @@ describe('service {{properCase service.name}} version {{service.version}} - comm const parameter: {{properCase service.name}}V{{service.version}}{{properCase name}}InputParameter = {} - const context = {{camelCase name}}CommandBuilder.getCommandContextMock(payload, parameter, sandbox) + const context = {{camelCase name}}CommandBuilder.getCommandContextMock({payload, parameter, sandbox}) const result = await {{camelCase name}}(context.mock, payload, parameter) diff --git a/packages/cli/blueprint/src/service/serviceName/v1/servicenameV1Builder.ts.hbs b/packages/cli/blueprint/src/service/serviceName/v1/servicenameV1Builder.ts.hbs index e04be3048..4ded872c7 100644 --- a/packages/cli/blueprint/src/service/serviceName/v1/servicenameV1Builder.ts.hbs +++ b/packages/cli/blueprint/src/service/serviceName/v1/servicenameV1Builder.ts.hbs @@ -11,4 +11,4 @@ export const {{camelCase name}}ServiceInfo = { // create a service builder instance and assign service config schema and default config. export const {{camelCase name}}V{{version}}ServiceBuilder = new ServiceBuilder({{camelCase name}}ServiceInfo).setConfigSchema({{camelCase name}}ServiceV{{version}}ConfigSchema) -.setDefaultConfig({}) + diff --git a/packages/cli/blueprint/src/service/serviceName/v1/subscription/name/name.test.ts.hbs b/packages/cli/blueprint/src/service/serviceName/v1/subscription/name/name.test.ts.hbs index 1bb66668f..736a235dc 100644 --- a/packages/cli/blueprint/src/service/serviceName/v1/subscription/name/name.test.ts.hbs +++ b/packages/cli/blueprint/src/service/serviceName/v1/subscription/name/name.test.ts.hbs @@ -11,9 +11,9 @@ import { {{camelCase name}}SubscriptionBuilder } from './{{camelCase name}}Subsc import { {{properCase service.name}}V{{service.version}}{{properCase name}}InputPayload, {{properCase service.name}}V{{service.version}}{{properCase name}}InputParameter } from './types{{fileExt}}' describe('service {{properCase service.name}} version {{service.version}} - subscription {{camelCase name}}', () => { - let sandbox = createSandbox() + const sandbox = createSandbox() beforeEach(() => { - sandbox = createSandbox() + // add your before each here }) afterEach(() => { @@ -22,7 +22,7 @@ describe('service {{properCase service.name}} version {{service.version}} - subs test('does not throw', async () => { // create a service instance to be bind to the subscription function - const service = {{camelCase service.name}}V{{service.version}}Service.getInstance(getEventBridgeMock(sandbox).mock, { + const service = await {{camelCase service.name}}V{{service.version}}Service.getInstance(getEventBridgeMock(sandbox).mock, { logger: getLoggerMock(sandbox).mock }) @@ -39,7 +39,7 @@ describe('service {{properCase service.name}} version {{service.version}} - subs const message = getCommandSuccessMessageMock(payload) // create a subscription context for the subscription function - const context = {{camelCase name}}SubscriptionBuilder.getSubscriptionContextMock(message, sandbox) + const context = {{camelCase name}}SubscriptionBuilder.getSubscriptionContextMock({message, sandbox}) // execute the subscription function const result = await {{camelCase name}}(context.mock, payload, parameter) diff --git a/packages/cli/jsr.json b/packages/cli/jsr.json new file mode 100644 index 000000000..424e2d477 --- /dev/null +++ b/packages/cli/jsr.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://jsr.io/schema/config-file.v1.json", + "name": "@purista/cli", + "version": "1.11.0", + "description": "cli helper for PURISTA backend framework", + "keywords": ["purista", "http", "typescript", "javascript"], + "exports": "./dist/esm/index.js", + "publish": { + "include": ["dist/**/*.js", "dist/**/*.d.ts", "README.md", "package.json"], + "exclude": [ + "src", + ".github", + ".vscode", + ".zed", + "!dist", + "!dist/**/*.js", + "!dist/**/*.d.ts", + ".tshy", + ".tshy-build", + "vendor", + "docs", + "typedoc.json", + "..eslintcache", + ".npmignore" + ] + } +} diff --git a/packages/cli/package.json b/packages/cli/package.json index 9e770d0ff..8dce9b878 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,59 +1,54 @@ { - "name": "@purista/cli", - "version": "1.11.0", - "description": "cli helper for PURISTA backend framework", - "homepage": "https://purista.dev", - "repository": { - "type": "git", - "url": "git@github.com:sebastianwessel/purista.git" - }, - "author": "Sebastian Wessel", - "license": "ISC", - "preferGlobal": "true", - "main": "dist/index.js", - "type": "module", - "bin": { - "purista": "./dist/index.js" - }, - "publishConfig": { - "access": "public" - }, - "engines": { - "node": ">=18.15" - }, - "scripts": { - "start": "./dist/index.js", - "dev": "tsx src/index.ts", - "build": "rimraf dist && tsc --project ./tsconfig.json && chmod 755 dist/index.js", - "lint": "eslint . --ext .ts,.json --cache . --fix", - "test": "vitest" - }, - "tshy": { - "exclude": [ - "src/**/*.test.ts" - ], - "exports": { - "./package.json": "./package.json", - ".": "./src/index.ts" - } - }, - "devDependencies": { - "@types/minimist": "^1.2.5", - "@types/node": "^20.11.17", - "rimraf": "^5.0.5", - "tshy": "^1.11.1", - "tsx": "^4.7.1" - }, - "dependencies": { - "camelcase": "^8.0.0", - "minimist": "^1.2.8", - "plop": "^4.0.1", - "ts-morph": "^21.0.1", - "zod": "^3.22.4" - }, - "peerDependenciesMeta": {}, - "files": [ - "blueprint/**/*", - "dist/**/*" - ] + "name": "@purista/cli", + "version": "1.11.0", + "description": "cli helper for PURISTA backend framework", + "homepage": "https://purista.dev", + "repository": { + "type": "git", + "url": "git@github.com:puristajs/purista.git" + }, + "author": "Sebastian Wessel", + "license": "ISC", + "preferGlobal": "true", + "main": "dist/index.js", + "type": "module", + "bin": { + "purista": "./dist/index.js" + }, + "publishConfig": { + "access": "public" + }, + "engines": { + "node": ">=18.15" + }, + "scripts": { + "start": "./dist/index.js", + "dev": "tsx src/index.ts", + "build": "rimraf dist && tsc --project ./tsconfig.json && chmod 755 dist/index.js", + "lint": "npx @biomejs/biome check --write", + "test": "vitest" + }, + "tshy": { + "exclude": ["src/**/*.test.ts"], + "exports": { + "./package.json": "./package.json", + ".": "./src/index.ts" + } + }, + "devDependencies": { + "@types/minimist": "^1.2.5", + "@types/node": "^22.5.1", + "rimraf": "^6.0.1", + "tshy": "^3.0.2", + "tsx": "^4.15.7" + }, + "dependencies": { + "camelcase": "^8.0.0", + "minimist": "^1.2.8", + "plop": "^4.0.1", + "ts-morph": "^25.0.0", + "zod": "^3.24.1" + }, + "peerDependenciesMeta": {}, + "files": ["blueprint/**/*", "dist/**/*"] } diff --git a/packages/cli/src/addResource/addCommandActions.ts b/packages/cli/src/addResource/addCommandActions.ts new file mode 100644 index 000000000..8d6579820 --- /dev/null +++ b/packages/cli/src/addResource/addCommandActions.ts @@ -0,0 +1,104 @@ +/* eslint-disable no-console */ +import camelCase from 'camelcase' +import type { Actions } from 'node-plop' + +import { TEMPLATE_BASE } from '../config.js' +import { collectInstallInfo, installInfo } from '../helper/installInfo.js' +import { addDefinitionToBuilder } from '../manipulation/addDefinitionToBuilder.js' +import { addEventEnumToCommandBuilder } from '../manipulation/addEventEnumToCommandBuilder.js' +import { ensureServiceEvent } from '../manipulation/ensureServiceEvent.js' +import { lintFiles } from '../manipulation/lintFiles.js' + +export const addCommandActions: Actions = [ + async () => { + await collectInstallInfo() + return 'checking current setup' + }, + { + type: 'add', + skipIfExists: true, + path: 'src/service/{{service.path}}/command/{{camelCase name}}/readme.md', + templateFile: `${TEMPLATE_BASE}/src/service/serviceName/v1/command/name/readme.md`, + }, + { + type: 'add', + skipIfExists: true, + path: 'src/service/{{service.path}}/command/{{camelCase name}}/index.ts', + templateFile: `${TEMPLATE_BASE}/src/service/serviceName/v1/command/name/index.ts.hbs`, + }, + { + type: 'add', + skipIfExists: true, + path: 'src/service/{{service.path}}/command/{{camelCase name}}/schema.ts', + templateFile: `${TEMPLATE_BASE}/src/service/serviceName/v1/command/name/schema.ts.hbs`, + }, + { + type: 'add', + skipIfExists: true, + path: 'src/service/{{service.path}}/command/{{camelCase name}}/types.ts', + templateFile: `${TEMPLATE_BASE}/src/service/serviceName/v1/command/name/types.ts.hbs`, + }, + { + type: 'add', + skipIfExists: true, + path: 'src/service/{{service.path}}/command/{{camelCase name}}/{{camelCase name}}.test.ts', + templateFile: `${TEMPLATE_BASE}/src/service/serviceName/v1/command/name/name.test.ts.hbs`, + skip: () => !installInfo.jestIsPresent || !installInfo.sinonIsPresent, + }, + { + type: 'add', + skipIfExists: true, + path: 'src/service/{{service.path}}/command/{{camelCase name}}/{{camelCase name}}CommandBuilder.ts', + templateFile: `${TEMPLATE_BASE}/src/service/serviceName/v1/command/name/nameBuilder.ts.hbs`, + }, + { + type: 'append', + path: 'src/service/{{service.path}}/index.ts', + templateFile: `${TEMPLATE_BASE}/src/service/serviceName/v1/partial_addTypeExportCommand.ts.hbs`, + }, + async answers => { + try { + const enumDescription = `Emitted by ${answers.service.name} v${answers.service.version} command ${camelCase( + answers.name, + )}:\n${answers.description}` + const eventEnumName = await ensureServiceEvent(answers.commandEventName, enumDescription) + if (eventEnumName) { + await addEventEnumToCommandBuilder( + `src/service/${answers.service.path}/command/${camelCase(answers.name)}/${camelCase( + answers.name, + )}CommandBuilder.ts`, + eventEnumName, + ) + } + + const serviceBuilderFile = `src/service/${answers.service.path}/${answers.service.serviceFile}` + await addDefinitionToBuilder( + 'commandDefinitions', + serviceBuilderFile, + `./command/${camelCase(answers.name)}/${camelCase(answers.name)}CommandBuilder${answers.isEsm ? '.js' : ''}`, + `${camelCase(answers.name)}CommandBuilder`, + ) + + const files: string[] = [ + `src/service/${answers.service.path}/command/${camelCase(answers.name)}/index.ts`, + `src/service/${answers.service.path}/command/${camelCase(answers.name)}/schema.ts`, + `src/service/${answers.service.path}/command/${camelCase(answers.name)}/types.ts`, + `src/service/${answers.service.path}/command/${camelCase(answers.name)}/${camelCase(answers.name)}.test.ts`, + `src/service/${answers.service.path}/command/${camelCase(answers.name)}/${camelCase( + answers.name, + )}CommandBuilder.ts`, + serviceBuilderFile, + `src/service/${answers.service.path}/index.ts`, + 'src/service/ServiceEvent.enum.ts', + ] + + await lintFiles(files) + } catch (error) { + return 'Please check manually!' + } + return 'files updated' + }, + answers => { + return '📖 Learn more about PURISTA at https://purista.dev' + }, +] diff --git a/packages/cli/src/addResource/addResourcePrompts.ts b/packages/cli/src/addResource/addResourcePrompts.ts new file mode 100644 index 000000000..fc89ed5b0 --- /dev/null +++ b/packages/cli/src/addResource/addResourcePrompts.ts @@ -0,0 +1,127 @@ +/* eslint-disable no-console */ +import path from 'node:path' + +import type { Answers } from 'inquirer' +import type { Prompts } from 'node-plop' + +import { getEventNames } from '../helper/getEventNames.js' +import { collectServices, installInfo } from '../helper/installInfo.js' + +export const addResourcePrompts: Prompts = [ + { + type: 'list', + message: 'What do you want to do?', + name: 'intention', + choices: [ + { value: 'add', name: 'add resource' }, + { value: 'init', name: 'init PURISTA' }, + ], + }, + { + type: 'list', + message: 'What do you want to do?', + name: 'resource', + choices: [ + { value: 'service', name: 'add new service' }, + { value: 'command', name: 'add a command to existing service' }, + { value: 'subscription', name: 'add a subscription to existing service' }, + ], + }, + { + type: 'input', + message(answers) { + switch (answers.resource) { + case 'service': + return 'What is the name (or domain) of your new service (something like: user or account)' + case 'command': + return 'What is the name of the new command' + case 'subscription': + return 'What is the name of the new subscription' + } + throw new Error('Invalid input: purista add [service|command|subscription]') + }, + name: 'name', + validate: (input: string) => { + const match = input.match(/^[a-zA-Z0-9\s\-_]+$/) + if (match?.length && match[0]) { + return true + } + return 'required: must match [a-zA-Z0-9 -_]' + }, + }, + { + type: 'input', + message(answers) { + switch (answers.resource) { + case 'service': + return `What is the matter of service "${answers.name}"` + case 'command': + return `What is the matter of command "${answers.name}"` + case 'subscription': + return `What is the matter of subscription "${answers.name}"` + } + throw new Error('Invalid input: purista add [service|command|subscription]') + }, + name: 'description', + }, + { + type: 'list', + message: 'Select a event to subscribe', + name: 'subscriptionEventList', + when: (answers: Record) => answers.resource === 'subscription' && getEventNames().length > 0, + choices: _answers => { + return [...getEventNames(), { name: 'Add a new event', value: '' }] + }, + }, + { + type: 'input', + message: 'Name of the event to listen for', + name: 'subscriptionEventName', + default: (answers: Record) => { + return answers.subscriptionEventList !== '' ? answers.subscriptionEventList : '' + }, + when: (answers: Record) => + answers.resource === 'subscription' && + (getEventNames().length === 0 || (answers.subscriptionEventList as string)?.trim() === ''), + }, + { + type: 'input', + message: 'Name of response event', + name: 'commandEventName', + when: (answers: Record) => answers.resource === 'command', + }, + { + type: 'input', + message: 'Version number of this service', + default: '1', + validate: (input: string) => { + const match = input.match(/^(\d+)$/) + let version = 0 + if (match?.length && match[0]) { + version = Number.parseInt(match[0]) + } + return version > 0 || 'version must be a a positiv int value larger than 0' + }, + name: 'version', + when: (answers: Record) => answers.resource === 'service', + }, + { + type: 'list', + message: 'select a service', + name: 'service', + when: (answers: Record) => answers.resource !== 'service', + async choices(_answers) { + const servicePath = path.join(process.cwd(), 'src', 'service') + collectServices(servicePath) + + const services: Answers[] = installInfo.services.map(entry => { + return { + name: `${entry.name} ${entry.version}`, + value: entry, + } + }) + + return services + }, + }, +] diff --git a/packages/cli/src/addResource/addServiceActions.ts b/packages/cli/src/addResource/addServiceActions.ts new file mode 100644 index 000000000..6622ec490 --- /dev/null +++ b/packages/cli/src/addResource/addServiceActions.ts @@ -0,0 +1,94 @@ +/* eslint-disable no-console */ +import camelCase from 'camelcase' +import type { Actions } from 'node-plop' + +import { TEMPLATE_BASE } from '../config.js' +import { lintFiles } from '../manipulation/lintFiles.js' + +export const addServiceActions: Actions = [ + { + type: 'add', + skipIfExists: true, + path: 'src/service/ServiceEvent.enum.ts', + templateFile: `${TEMPLATE_BASE}/src/service/serviceEvent.enum.ts.hbs`, + }, + { + type: 'add', + skipIfExists: true, + path: 'src/service/{{camelCase name}}/readme.md', + templateFile: `${TEMPLATE_BASE}/src/service/serviceName/readme.md`, + }, + { + type: 'add', + skipIfExists: true, + path: 'src/service/{{camelCase name}}/general{{properCase name}}ServiceInfo.ts', + templateFile: `${TEMPLATE_BASE}/src/service/serviceName/generalServicenameServiceInfo.ts.hbs`, + }, + { + type: 'add', + skipIfExists: true, + path: 'src/service/{{camelCase name}}/v{{version}}/readme.md', + templateFile: `${TEMPLATE_BASE}/src/service/serviceName/v1/readme.md`, + }, + { + type: 'add', + skipIfExists: true, + path: 'src/service/{{camelCase name}}/v{{version}}/{{camelCase name}}V{{version}}Service.ts', + templateFile: `${TEMPLATE_BASE}/src/service/serviceName/v1/servicenameV1Service.ts.hbs`, + }, + { + type: 'add', + skipIfExists: true, + path: 'src/service/{{camelCase name}}/v{{version}}/{{camelCase name}}V{{version}}Service.test.ts', + templateFile: `${TEMPLATE_BASE}/src/service/serviceName/v1/servicenameV1Service.test.ts.hbs`, + }, + { + type: 'add', + skipIfExists: true, + path: 'src/service/{{camelCase name}}/v{{version}}/{{camelCase name}}V{{version}}ServiceBuilder.ts', + templateFile: `${TEMPLATE_BASE}/src/service/serviceName/v1/servicenameV1Builder.ts.hbs`, + }, + { + type: 'add', + skipIfExists: true, + path: 'src/service/{{camelCase name}}/v{{version}}/index.ts', + templateFile: `${TEMPLATE_BASE}/src/service/serviceName/v1/index.ts.hbs`, + }, + { + type: 'add', + skipIfExists: true, + path: 'src/service/{{camelCase name}}/v{{version}}/{{camelCase name}}ServiceConfig.ts', + templateFile: `${TEMPLATE_BASE}/src/service/serviceName/v1/serviceNameServiceConfig.ts.hbs`, + }, + async answers => { + try { + const files: string[] = [ + `src/service/${camelCase(answers.name, { pascalCase: false })}/general${camelCase(answers.name, { + pascalCase: true, + })}ServiceInfo.ts`, + `src/service/${camelCase(answers.name, { pascalCase: false })}/v${answers.version}/${camelCase(answers.name)}V${ + answers.version + }Service.ts`, + `src/service/${camelCase(answers.name, { pascalCase: false })}/v${answers.version}/${camelCase(answers.name)}V${ + answers.version + }Service.test.ts`, + `src/service/${camelCase(answers.name, { pascalCase: false })}/v${answers.version}/${camelCase(answers.name)}V${ + answers.version + }ServiceBuilder.ts`, + `src/service/${camelCase(answers.name, { pascalCase: false })}/v${answers.version}/${camelCase( + answers.name, + )}ServiceConfig.ts`, + `src/service/${camelCase(answers.name, { pascalCase: false })}/v${answers.version}/index.ts`, + 'src/service/ServiceEvent.enum.ts', + ] + + await lintFiles(files) + } catch (error) { + return 'Please check manually!' + } + return 'files updated' + }, + answers => { + return '📖 Learn more about PURISTA at https://purista.dev' + }, +] diff --git a/packages/cli/src/addResource/addSubscriptionActions.ts b/packages/cli/src/addResource/addSubscriptionActions.ts new file mode 100644 index 000000000..eb5d3d698 --- /dev/null +++ b/packages/cli/src/addResource/addSubscriptionActions.ts @@ -0,0 +1,105 @@ +/* eslint-disable no-console */ +import camelCase from 'camelcase' +import type { Actions } from 'node-plop' + +import { TEMPLATE_BASE } from '../config.js' +import { collectInstallInfo, installInfo } from '../helper/installInfo.js' +import { addDefinitionToBuilder } from '../manipulation/addDefinitionToBuilder.js' +import { addEventEnumToSubscriptionBuilder } from '../manipulation/addEventEnumToSubscriptionBuilder.js' +import { ensureServiceEvent } from '../manipulation/ensureServiceEvent.js' +import { lintFiles } from '../manipulation/lintFiles.js' + +export const addSubscriptionActions: Actions = [ + async () => { + await collectInstallInfo() + return 'checking current setup' + }, + { + type: 'add', + skipIfExists: true, + path: 'src/service/{{service.path}}/subscription/{{camelCase name}}/readme.md', + templateFile: `${TEMPLATE_BASE}/src/service/serviceName/v1/subscription/name/readme.md`, + }, + { + type: 'add', + skipIfExists: true, + path: 'src/service/{{service.path}}/subscription/{{camelCase name}}/index.ts', + templateFile: `${TEMPLATE_BASE}/src/service/serviceName/v1/subscription/name/index.ts.hbs`, + }, + { + type: 'add', + skipIfExists: true, + path: 'src/service/{{service.path}}/subscription/{{camelCase name}}/{{camelCase name}}.test.ts', + templateFile: `${TEMPLATE_BASE}/src/service/serviceName/v1/subscription/name/name.test.ts.hbs`, + skip: () => !installInfo.jestIsPresent || !installInfo.sinonIsPresent, + }, + { + type: 'add', + skipIfExists: true, + path: 'src/service/{{service.path}}/subscription/{{camelCase name}}/schema.ts', + templateFile: `${TEMPLATE_BASE}/src/service/serviceName/v1/subscription/name/schema.ts.hbs`, + skip: () => !installInfo.jestIsPresent || !installInfo.sinonIsPresent, + }, + { + type: 'add', + skipIfExists: true, + path: 'src/service/{{service.path}}/subscription/{{camelCase name}}/types.ts', + templateFile: `${TEMPLATE_BASE}/src/service/serviceName/v1/subscription/name/types.ts.hbs`, + skip: () => !installInfo.jestIsPresent || !installInfo.sinonIsPresent, + }, + { + type: 'add', + skipIfExists: true, + path: 'src/service/{{service.path}}/subscription/{{camelCase name}}/{{camelCase name}}SubscriptionBuilder.ts', + templateFile: `${TEMPLATE_BASE}/src/service/serviceName/v1/subscription/name/nameBuilder.ts.hbs`, + }, + { + type: 'append', + path: 'src/service/{{service.path}}/index.ts', + templateFile: `${TEMPLATE_BASE}/src/service/serviceName/v1/partial_addTypeExportSubscription.ts.hbs`, + }, + async answers => { + try { + const eventEnumName = await ensureServiceEvent(answers.subscriptionEventName || answers.subscriptionEventList) + if (eventEnumName) { + await addEventEnumToSubscriptionBuilder( + `src/service/${answers.service.path}/subscription/${camelCase(answers.name)}/${camelCase( + answers.name, + )}SubscriptionBuilder.ts`, + eventEnumName, + ) + } + + const serviceBuilderFile = `src/service/${answers.service.path}/${answers.service.serviceFile}` + await addDefinitionToBuilder( + 'subscriptionDefinitions', + serviceBuilderFile, + `./subscription/${camelCase(answers.name)}/${camelCase(answers.name)}SubscriptionBuilder${answers.isEsm ? '.js' : ''}`, + `${camelCase(answers.name)}SubscriptionBuilder`, + ) + + const files: string[] = [ + `src/service/${answers.service.path}/subscription/${camelCase(answers.name)}/index.ts`, + `src/service/${answers.service.path}/subscription/${camelCase(answers.name)}/schema.ts`, + `src/service/${answers.service.path}/subscription/${camelCase(answers.name)}/types.ts`, + `src/service/${answers.service.path}/subscription/${camelCase(answers.name)}/${camelCase( + answers.name, + )}.test.ts`, + `src/service/${answers.service.path}/subscription/${camelCase(answers.name)}/${camelCase( + answers.name, + )}SubscriptionBuilder.ts`, + serviceBuilderFile, + `src/service/${answers.service.path}/index.ts`, + 'src/service/ServiceEvent.enum.ts', + ] + + await lintFiles(files) + } catch (error) { + return 'Please check manually!' + } + return 'files updated' + }, + answers => { + return '📖 Learn more about PURISTA at https://purista.dev' + }, +] diff --git a/packages/cli/src/addResource/addVersionActions.ts b/packages/cli/src/addResource/addVersionActions.ts new file mode 100644 index 000000000..e06ac172e --- /dev/null +++ b/packages/cli/src/addResource/addVersionActions.ts @@ -0,0 +1,8 @@ +/* eslint-disable no-console */ +import type { Actions } from 'node-plop' + +export const addVersionActions: Actions = [ + answers => { + return '📖 Learn more about PURISTA at https://purista.dev' + }, +] diff --git a/packages/cli/src/addResource/plopfile.ts b/packages/cli/src/addResource/plopfile.ts new file mode 100644 index 000000000..e0de3a625 --- /dev/null +++ b/packages/cli/src/addResource/plopfile.ts @@ -0,0 +1,50 @@ +import type { NodePlopAPI } from 'plop' + +import { loadPackageJson } from '../helper/loadPackageJson.js' +import { registerHandlebarHelpers } from '../helper/registerHandlebarHelpers.js' +import { addCommandActions } from './addCommandActions.js' +import { addResourcePrompts } from './addResourcePrompts.js' +import { addServiceActions } from './addServiceActions.js' +import { addSubscriptionActions } from './addSubscriptionActions.js' +import { addVersionActions } from './addVersionActions.js' + +export default function (plop: NodePlopAPI) { + registerHandlebarHelpers(plop) + plop.setActionType('select action', answers => { + return JSON.stringify(answers) + }) + + plop.setWelcomeMessage('Welcome to PURISTA cli') + + plop.setGenerator('rootMenu', { + description: 'Add a resource to current PURISTA project', + prompts: addResourcePrompts, + actions: function (answers: any) { + const actions: any[] = [] + + const packageJson = loadPackageJson(process.cwd()) + + const isEsm = packageJson.type === 'module' + answers.fileExt = isEsm ? '.js' : '' + answers.indexExt = isEsm ? '/index.js' : '' + answers.isEsm = isEsm + + switch (answers.resource) { + case 'service': + actions.push(...(addServiceActions as [])) + break + case 'command': + actions.push(...(addCommandActions as [])) + break + case 'subscription': + actions.push(...(addSubscriptionActions as [])) + break + case 'version': + actions.push(...(addVersionActions as [])) + break + } + + return actions + }, + }) +} diff --git a/packages/cli/src/addRessource/addCommandActions.ts b/packages/cli/src/addRessource/addCommandActions.ts deleted file mode 100644 index dd51533ae..000000000 --- a/packages/cli/src/addRessource/addCommandActions.ts +++ /dev/null @@ -1,126 +0,0 @@ -/* eslint-disable no-console */ -import camelCase from 'camelcase' -import type { Actions } from 'node-plop' - -import { TEMPLATE_BASE } from '../config.js' -import { collectInstallInfo, installInfo } from '../helper/installInfo.js' -import { addDefinitionToBuilder } from '../manipulation/addDefinitionToBuilder.js' -import { addEventEnumToCommandBuilder } from '../manipulation/addEventEnumToCommandBuilder.js' -import { ensureServiceEvent } from '../manipulation/ensureServiceEvent.js' -import { lintFiles } from '../manipulation/lintFiles.js' - -export const addCommandActions: Actions = [ - async () => { - await collectInstallInfo() - return 'checking current setup' - }, - { - type: 'add', - skipIfExists: true, - path: 'src/service/{{service.path}}/command/{{camelCase name}}/readme.md', - templateFile: TEMPLATE_BASE + '/src/service/serviceName/v1/command/name/readme.md', - }, - { - type: 'add', - skipIfExists: true, - path: 'src/service/{{service.path}}/command/{{camelCase name}}/index.ts', - templateFile: TEMPLATE_BASE + '/src/service/serviceName/v1/command/name/index.ts.hbs', - }, - { - type: 'add', - skipIfExists: true, - path: 'src/service/{{service.path}}/command/{{camelCase name}}/schema.ts', - templateFile: TEMPLATE_BASE + '/src/service/serviceName/v1/command/name/schema.ts.hbs', - }, - { - type: 'add', - skipIfExists: true, - path: 'src/service/{{service.path}}/command/{{camelCase name}}/types.ts', - templateFile: TEMPLATE_BASE + '/src/service/serviceName/v1/command/name/types.ts.hbs', - }, - { - type: 'add', - skipIfExists: true, - path: 'src/service/{{service.path}}/command/{{camelCase name}}/{{camelCase name}}.test.ts', - templateFile: TEMPLATE_BASE + '/src/service/serviceName/v1/command/name/name.test.ts.hbs', - skip: () => !installInfo.jestIsPresent || !installInfo.sinonIsPresent, - }, - { - type: 'add', - skipIfExists: true, - path: 'src/service/{{service.path}}/command/{{camelCase name}}/{{camelCase name}}CommandBuilder.ts', - templateFile: TEMPLATE_BASE + '/src/service/serviceName/v1/command/name/nameBuilder.ts.hbs', - }, - { - type: 'append', - path: 'src/service/{{service.path}}/index.ts', - templateFile: TEMPLATE_BASE + '/src/service/serviceName/v1/partial_addTypeExportCommand.ts.hbs', - }, - async (answers) => { - console.log('try to update existing files - pls be patient!') - try { - const enumDescription = `Emitted by ${answers.service.name} v${answers.service.version} command ${camelCase( - answers.name, - )}:\n${answers.description}` - const eventEnumName = await ensureServiceEvent(answers.commandEventName, enumDescription) - if (eventEnumName) { - await addEventEnumToCommandBuilder( - `src/service/${answers.service.path}/command/${camelCase(answers.name)}/${camelCase( - answers.name, - )}CommandBuilder.ts`, - eventEnumName, - ) - } - - const serviceBuilderFile = `src/service/${answers.service.path}/${answers.service.serviceFile}` - await addDefinitionToBuilder( - 'commandDefinitions', - serviceBuilderFile, - `./command/${camelCase(answers.name)}/${camelCase(answers.name)}CommandBuilder${answers.isEsm ? '.js' : ''}`, - `${camelCase(answers.name)}CommandBuilder`, - ) - - const files: string[] = [ - `src/service/${answers.service.path}/command/${camelCase(answers.name)}/index.ts`, - `src/service/${answers.service.path}/command/${camelCase(answers.name)}/schema.ts`, - `src/service/${answers.service.path}/command/${camelCase(answers.name)}/types.ts`, - `src/service/${answers.service.path}/command/${camelCase(answers.name)}/${camelCase(answers.name)}.test.ts`, - `src/service/${answers.service.path}/command/${camelCase(answers.name)}/${camelCase( - answers.name, - )}CommandBuilder.ts`, - serviceBuilderFile, - `src/service/${answers.service.path}/index.ts`, - `src/service/ServiceEvent.enum.ts`, - ] - - await lintFiles(files) - } catch (error) { - console.log(error) - return 'Please check manually!' - } - return 'files updated' - }, - (answers) => { - console.log('') - console.log('') - console.log( - '🎉 The command "' + - answers.name + - '" in service "' + - answers.service.name + - '" version' + - answers.service.version + - ' is created 🎉', - ) - console.log('') - console.log('') - console.log('start adding your business logic here:') - console.log( - `./src/service/${answers.service.path}/command/${camelCase(answers.name)}/${camelCase( - answers.name, - )}CommandBuilder.ts`, - ) - console.log('') - return '📖 Learn more about PURISTA at https://purista.dev' - }, -] diff --git a/packages/cli/src/addRessource/addRessourcePrompts.ts b/packages/cli/src/addRessource/addRessourcePrompts.ts deleted file mode 100644 index 311e15550..000000000 --- a/packages/cli/src/addRessource/addRessourcePrompts.ts +++ /dev/null @@ -1,127 +0,0 @@ -/* eslint-disable no-console */ -import path from 'node:path' - -import type { Answers } from 'inquirer' -import type { Prompts } from 'node-plop' - -import { getEventNames } from '../helper/getEventNames.js' -import { collectServices, installInfo } from '../helper/installInfo.js' - -export const addRessourcePrompts: Prompts = [ - { - type: 'list', - message: 'What do you want to do?', - name: 'intention', - choices: [ - { value: 'add', name: 'add ressource' }, - { value: 'init', name: 'init PURISTA' }, - ], - }, - { - type: 'list', - message: 'What do you want to do?', - name: 'ressource', - choices: [ - { value: 'service', name: 'add new service' }, - { value: 'command', name: 'add a command to existing service' }, - { value: 'subscription', name: 'add a subscription to existing service' }, - ], - }, - { - type: 'input', - message(answers) { - switch (answers.ressource) { - case 'service': - return 'What is the name (or domain) of your new service (something like: user or account)' - case 'command': - return 'What is the name of the new command' - case 'subscription': - return 'What is the name of the new subscription' - } - throw new Error('Invalid input: purista add [service|command|subscription]') - }, - name: 'name', - validate: (input: string) => { - const match = input.match(/^[a-zA-Z0-9\s\-_]+$/) - if (match && match.length && match[0]) { - return true - } - return 'required: must match [a-zA-Z0-9 -_]' - }, - }, - { - type: 'input', - message(answers) { - switch (answers.ressource) { - case 'service': - return `What is the matter of service "${answers.name}"` - case 'command': - return `What is the matter of command "${answers.name}"` - case 'subscription': - return `What is the matter of subscription "${answers.name}"` - } - throw new Error('Invalid input: purista add [service|command|subscription]') - }, - name: 'description', - }, - { - type: 'list', - message: 'Select a event to subscribe', - name: 'subscriptionEventList', - when: (answers: Record) => answers.ressource === 'subscription' && getEventNames().length > 0, - choices: (_answers) => { - return [...getEventNames(), { name: 'Add a new event', value: '' }] - }, - }, - { - type: 'input', - message: 'Name of the event to listen for', - name: 'subscriptionEventName', - default: (answers: Record) => { - return answers.subscriptionEventList !== '' ? answers.subscriptionEventList : '' - }, - when: (answers: Record) => - answers.ressource === 'subscription' && - (getEventNames().length === 0 || (answers.subscriptionEventList as string)?.trim() === ''), - }, - { - type: 'input', - message: 'Name of response event', - name: 'commandEventName', - when: (answers: Record) => answers.ressource === 'command', - }, - { - type: 'input', - message: 'Version number of this service', - default: '1', - validate: (input: string) => { - const match = input.match(/^(\d+)$/) - let version = 0 - if (match && match.length && match[0]) { - version = parseInt(match[0]) - } - return version > 0 || 'version must be a a positiv int value larger than 0' - }, - name: 'version', - when: (answers: Record) => answers.ressource === 'service', - }, - { - type: 'list', - message: 'select a service', - name: 'service', - when: (answers: Record) => answers.ressource !== 'service', - async choices(_answers) { - const servicePath = path.join(process.cwd(), 'src', 'service') - collectServices(servicePath) - - const services: Answers[] = installInfo.services.map((entry) => { - return { - name: entry.name + ' ' + entry.version, - value: entry, - } - }) - - return services - }, - }, -] diff --git a/packages/cli/src/addRessource/addServiceActions.ts b/packages/cli/src/addRessource/addServiceActions.ts deleted file mode 100644 index 489d10536..000000000 --- a/packages/cli/src/addRessource/addServiceActions.ts +++ /dev/null @@ -1,103 +0,0 @@ -/* eslint-disable no-console */ -import camelCase from 'camelcase' -import type { Actions } from 'node-plop' - -import { TEMPLATE_BASE } from '../config.js' -import { lintFiles } from '../manipulation/lintFiles.js' - -export const addServiceActions: Actions = [ - { - type: 'add', - skipIfExists: true, - path: 'src/service/ServiceEvent.enum.ts', - templateFile: TEMPLATE_BASE + '/src/service/serviceEvent.enum.ts.hbs', - }, - { - type: 'add', - skipIfExists: true, - path: 'src/service/{{camelCase name}}/readme.md', - templateFile: TEMPLATE_BASE + '/src/service/serviceName/readme.md', - }, - { - type: 'add', - skipIfExists: true, - path: 'src/service/{{camelCase name}}/general{{properCase name}}ServiceInfo.ts', - templateFile: TEMPLATE_BASE + '/src/service/serviceName/generalServicenameServiceInfo.ts.hbs', - }, - { - type: 'add', - skipIfExists: true, - path: 'src/service/{{camelCase name}}/v{{version}}/readme.md', - templateFile: TEMPLATE_BASE + '/src/service/serviceName/v1/readme.md', - }, - { - type: 'add', - skipIfExists: true, - path: 'src/service/{{camelCase name}}/v{{version}}/{{camelCase name}}V{{version}}Service.ts', - templateFile: TEMPLATE_BASE + '/src/service/serviceName/v1/servicenameV1Service.ts.hbs', - }, - { - type: 'add', - skipIfExists: true, - path: 'src/service/{{camelCase name}}/v{{version}}/{{camelCase name}}V{{version}}Service.test.ts', - templateFile: TEMPLATE_BASE + '/src/service/serviceName/v1/servicenameV1Service.test.ts.hbs', - }, - { - type: 'add', - skipIfExists: true, - path: 'src/service/{{camelCase name}}/v{{version}}/{{camelCase name}}V{{version}}ServiceBuilder.ts', - templateFile: TEMPLATE_BASE + '/src/service/serviceName/v1/servicenameV1Builder.ts.hbs', - }, - { - type: 'add', - skipIfExists: true, - path: 'src/service/{{camelCase name}}/v{{version}}/index.ts', - templateFile: TEMPLATE_BASE + '/src/service/serviceName/v1/index.ts.hbs', - }, - { - type: 'add', - skipIfExists: true, - path: 'src/service/{{camelCase name}}/v{{version}}/{{camelCase name}}ServiceConfig.ts', - templateFile: TEMPLATE_BASE + '/src/service/serviceName/v1/serviceNameServiceConfig.ts.hbs', - }, - async (answers) => { - console.log('try to update existing files - pls be patient!') - try { - const files: string[] = [ - `src/service/${camelCase(answers.name, { pascalCase: false })}/general${camelCase(answers.name, { - pascalCase: true, - })}ServiceInfo.ts`, - `src/service/${camelCase(answers.name, { pascalCase: false })}/v${answers.version}/${camelCase(answers.name)}V${ - answers.version - }Service.ts`, - `src/service/${camelCase(answers.name, { pascalCase: false })}/v${answers.version}/${camelCase(answers.name)}V${ - answers.version - }Service.test.ts`, - `src/service/${camelCase(answers.name, { pascalCase: false })}/v${answers.version}/${camelCase(answers.name)}V${ - answers.version - }ServiceBuilder.ts`, - `src/service/${camelCase(answers.name, { pascalCase: false })}/v${answers.version}/${camelCase( - answers.name, - )}ServiceConfig.ts`, - `src/service/${camelCase(answers.name, { pascalCase: false })}/v${answers.version}/index.ts`, - `src/service/ServiceEvent.enum.ts`, - ] - - await lintFiles(files) - } catch (error) { - console.log(error) - return 'Please check manually!' - } - return 'files updated' - }, - (answers) => { - console.log('') - console.log('🎉 The service ' + answers.name + ' v1 is created 🎉') - console.log('Now it is time to add a command or subscription to the service') - console.log('') - console.log('➡️ purista add command') - console.log('➡️ purista add subscription') - console.log('') - return '📖 Learn more about PURISTA at https://purista.dev' - }, -] diff --git a/packages/cli/src/addRessource/addSubscriptionActions.ts b/packages/cli/src/addRessource/addSubscriptionActions.ts deleted file mode 100644 index 1fe3ca0d3..000000000 --- a/packages/cli/src/addRessource/addSubscriptionActions.ts +++ /dev/null @@ -1,126 +0,0 @@ -/* eslint-disable no-console */ -import camelCase from 'camelcase' -import type { Actions } from 'node-plop' - -import { TEMPLATE_BASE } from '../config.js' -import { collectInstallInfo, installInfo } from '../helper/installInfo.js' -import { addDefinitionToBuilder } from '../manipulation/addDefinitionToBuilder.js' -import { addEventEnumToSubscriptionBuilder } from '../manipulation/addEventEnumToSubscriptionBuilder.js' -import { ensureServiceEvent } from '../manipulation/ensureServiceEvent.js' -import { lintFiles } from '../manipulation/lintFiles.js' - -export const addSubscriptionActions: Actions = [ - async () => { - await collectInstallInfo() - return 'checking current setup' - }, - { - type: 'add', - skipIfExists: true, - path: 'src/service/{{service.path}}/subscription/{{camelCase name}}/readme.md', - templateFile: TEMPLATE_BASE + '/src/service/serviceName/v1/subscription/name/readme.md', - }, - { - type: 'add', - skipIfExists: true, - path: 'src/service/{{service.path}}/subscription/{{camelCase name}}/index.ts', - templateFile: TEMPLATE_BASE + '/src/service/serviceName/v1/subscription/name/index.ts.hbs', - }, - { - type: 'add', - skipIfExists: true, - path: 'src/service/{{service.path}}/subscription/{{camelCase name}}/{{camelCase name}}.test.ts', - templateFile: TEMPLATE_BASE + '/src/service/serviceName/v1/subscription/name/name.test.ts.hbs', - skip: () => !installInfo.jestIsPresent || !installInfo.sinonIsPresent, - }, - { - type: 'add', - skipIfExists: true, - path: 'src/service/{{service.path}}/subscription/{{camelCase name}}/schema.ts', - templateFile: TEMPLATE_BASE + '/src/service/serviceName/v1/subscription/name/schema.ts.hbs', - skip: () => !installInfo.jestIsPresent || !installInfo.sinonIsPresent, - }, - { - type: 'add', - skipIfExists: true, - path: 'src/service/{{service.path}}/subscription/{{camelCase name}}/types.ts', - templateFile: TEMPLATE_BASE + '/src/service/serviceName/v1/subscription/name/types.ts.hbs', - skip: () => !installInfo.jestIsPresent || !installInfo.sinonIsPresent, - }, - { - type: 'add', - skipIfExists: true, - path: 'src/service/{{service.path}}/subscription/{{camelCase name}}/{{camelCase name}}SubscriptionBuilder.ts', - templateFile: TEMPLATE_BASE + '/src/service/serviceName/v1/subscription/name/nameBuilder.ts.hbs', - }, - { - type: 'append', - path: 'src/service/{{service.path}}/index.ts', - templateFile: TEMPLATE_BASE + '/src/service/serviceName/v1/partial_addTypeExportSubscription.ts.hbs', - }, - async (answers) => { - console.log('try to update existing files - pls be patient!') - try { - const eventEnumName = await ensureServiceEvent(answers.subscriptionEventName || answers.subscriptionEventList) - if (eventEnumName) { - await addEventEnumToSubscriptionBuilder( - `src/service/${answers.service.path}/subscription/${camelCase(answers.name)}/${camelCase( - answers.name, - )}SubscriptionBuilder.ts`, - eventEnumName, - ) - } - - const serviceBuilderFile = `src/service/${answers.service.path}/${answers.service.serviceFile}` - await addDefinitionToBuilder( - 'subscriptionDefinitions', - serviceBuilderFile, - `./subscription/${camelCase(answers.name)}/${camelCase(answers.name)}SubscriptionBuilder${answers.isEsm ? '.js' : ''}`, - `${camelCase(answers.name)}SubscriptionBuilder`, - ) - - const files: string[] = [ - `src/service/${answers.service.path}/subscription/${camelCase(answers.name)}/index.ts`, - `src/service/${answers.service.path}/subscription/${camelCase(answers.name)}/schema.ts`, - `src/service/${answers.service.path}/subscription/${camelCase(answers.name)}/types.ts`, - `src/service/${answers.service.path}/subscription/${camelCase(answers.name)}/${camelCase( - answers.name, - )}.test.ts`, - `src/service/${answers.service.path}/subscription/${camelCase(answers.name)}/${camelCase( - answers.name, - )}SubscriptionBuilder.ts`, - serviceBuilderFile, - `src/service/${answers.service.path}/index.ts`, - `src/service/ServiceEvent.enum.ts`, - ] - - await lintFiles(files) - } catch (error) { - console.log(error) - return 'Please check manually!' - } - return 'files updated' - }, - (answers) => { - console.log('') - console.log( - '🎉 The subscription ' + - answers.name + - ' in ' + - answers.service.name + - ' v' + - answers.service.version + - ' is created 🎉', - ) - console.log('') - console.log('') - console.log('start adding your business logic here:') - console.log( - `./src/service/${answers.service.path}/subscription/${camelCase(answers.name)}/${camelCase( - answers.name, - )}SubscriptionBuilder.ts`, - ) - console.log('') - return '📖 Learn more about PURISTA at https://purista.dev' - }, -] diff --git a/packages/cli/src/addRessource/addVersionActions.ts b/packages/cli/src/addRessource/addVersionActions.ts deleted file mode 100644 index 0484202fe..000000000 --- a/packages/cli/src/addRessource/addVersionActions.ts +++ /dev/null @@ -1,11 +0,0 @@ -/* eslint-disable no-console */ -import type { Actions } from 'node-plop' - -export const addVersionActions: Actions = [ - (answers) => { - console.log('') - console.log('🎉 The new version of ' + answers.service.name + ' is created 🎉') - console.log('') - return '📖 Learn more about PURISTA at https://purista.dev' - }, -] diff --git a/packages/cli/src/addRessource/plopfile.ts b/packages/cli/src/addRessource/plopfile.ts deleted file mode 100644 index f6d482df2..000000000 --- a/packages/cli/src/addRessource/plopfile.ts +++ /dev/null @@ -1,50 +0,0 @@ -import type { NodePlopAPI } from 'plop' - -import { loadPackageJson } from '../helper/loadPackageJson.js' -import { registerHandlebarHelpers } from '../helper/registerHandlebarHelpers.js' -import { addCommandActions } from './addCommandActions.js' -import { addRessourcePrompts } from './addRessourcePrompts.js' -import { addServiceActions } from './addServiceActions.js' -import { addSubscriptionActions } from './addSubscriptionActions.js' -import { addVersionActions } from './addVersionActions.js' - -export default function (plop: NodePlopAPI) { - registerHandlebarHelpers(plop) - plop.setActionType('select action', (answers) => { - return JSON.stringify(answers) - }) - - plop.setWelcomeMessage('Welcome to PURISTA cli') - - plop.setGenerator('rootMenu', { - description: 'Add a ressource to current PURISTA project', - prompts: addRessourcePrompts, - actions: function (answers: any) { - const actions: any[] = [] - - const packageJson = loadPackageJson(process.cwd()) - - const isEsm = packageJson['type'] === 'module' - answers.fileExt = isEsm ? '.js' : '' - answers.indexExt = isEsm ? '/index.js' : '' - answers.isEsm = isEsm - - switch (answers.ressource) { - case 'service': - actions.push(...(addServiceActions as [])) - break - case 'command': - actions.push(...(addCommandActions as [])) - break - case 'subscription': - actions.push(...(addSubscriptionActions as [])) - break - case 'version': - actions.push(...(addVersionActions as [])) - break - } - - return actions - }, - }) -} diff --git a/packages/cli/src/config.ts b/packages/cli/src/config.ts index 6d9ded522..0edd196de 100644 --- a/packages/cli/src/config.ts +++ b/packages/cli/src/config.ts @@ -12,17 +12,17 @@ export const jestDependencies: string[] = ['@swc/jest', '@types/jest', 'jest'] export const vitestDependencies: string[] = ['vitest'] export const eslintDependencies: string[] = [ - '@typescript-eslint/eslint-plugin', - '@typescript-eslint/parser', - 'eslint', - 'eslint-config-prettier', - 'eslint-config-standard', - 'eslint-plugin-import', - 'eslint-plugin-json', - 'eslint-plugin-node', - 'eslint-plugin-prettier', - 'eslint-plugin-simple-import-sort', - 'prettier', + '@typescript-eslint/eslint-plugin', + '@typescript-eslint/parser', + 'eslint', + 'eslint-config-prettier', + 'eslint-config-standard', + 'eslint-plugin-import', + 'eslint-plugin-json', + 'eslint-plugin-node', + 'eslint-plugin-prettier', + 'eslint-plugin-simple-import-sort', + 'prettier', ] export const biomeDependencies: string[] = ['biome'] diff --git a/packages/cli/src/helper/getEventNames.ts b/packages/cli/src/helper/getEventNames.ts index 53b7ea2bc..7437ac923 100644 --- a/packages/cli/src/helper/getEventNames.ts +++ b/packages/cli/src/helper/getEventNames.ts @@ -4,47 +4,47 @@ import { Project } from 'ts-morph' export let eventNames: { name: string; value: string }[] export const getEventNames = (): { name: string; value: string }[] => { - if (eventNames) { - return eventNames - } - try { - const tsConfigFilePath = join(process.cwd(), 'tsconfig.json') - const project = new Project({ - tsConfigFilePath, - }) - - const enumFile = join('.', 'src', 'service', 'ServiceEvent.enum.ts') - - const sourceFile = project.addSourceFileAtPathIfExists(enumFile) - - if (!sourceFile) { - eventNames = [] - return eventNames - } - - const serviceEventEnum = sourceFile.getEnum('ServiceEvent') - - if (!serviceEventEnum) { - eventNames = [] - return eventNames - } - - eventNames = serviceEventEnum - .getMembers() - .map((member) => ({ value: member.getValue() as string, name: member.getValue() as string })) - .sort((a, b) => { - if (a.value < b.value) { - return -1 - } - if (a.value > b.value) { - return 1 - } - return 0 - }) - - return eventNames - } catch (error) { - eventNames = [] - return eventNames - } + if (eventNames) { + return eventNames + } + try { + const tsConfigFilePath = join(process.cwd(), 'tsconfig.json') + const project = new Project({ + tsConfigFilePath, + }) + + const enumFile = join('.', 'src', 'service', 'ServiceEvent.enum.ts') + + const sourceFile = project.addSourceFileAtPathIfExists(enumFile) + + if (!sourceFile) { + eventNames = [] + return eventNames + } + + const serviceEventEnum = sourceFile.getEnum('ServiceEvent') + + if (!serviceEventEnum) { + eventNames = [] + return eventNames + } + + eventNames = serviceEventEnum + .getMembers() + .map(member => ({ value: member.getValue() as string, name: member.getValue() as string })) + .sort((a, b) => { + if (a.value < b.value) { + return -1 + } + if (a.value > b.value) { + return 1 + } + return 0 + }) + + return eventNames + } catch (error) { + eventNames = [] + return eventNames + } } diff --git a/packages/cli/src/helper/installDependencies.ts b/packages/cli/src/helper/installDependencies.ts index 54b97d439..fc7768bb3 100644 --- a/packages/cli/src/helper/installDependencies.ts +++ b/packages/cli/src/helper/installDependencies.ts @@ -1,14 +1,13 @@ import { exec } from 'node:child_process' export const installDependencies = async (cmd: string) => { - const child = exec(cmd, (err) => { - if (err) { - // eslint-disable-next-line no-console - console.error(err) - throw err - } - }) - child.stderr?.pipe(process.stderr) - child.stdout?.pipe(process.stdout) - return new Promise((resolve) => child.on('close', resolve)) + const child = exec(cmd, err => { + if (err) { + console.error(err) + throw err + } + }) + child.stderr?.pipe(process.stderr) + child.stdout?.pipe(process.stdout) + return new Promise(resolve => child.on('close', resolve)) } diff --git a/packages/cli/src/helper/installInfo.ts b/packages/cli/src/helper/installInfo.ts index 7b40f9c32..d9c01ceb9 100644 --- a/packages/cli/src/helper/installInfo.ts +++ b/packages/cli/src/helper/installInfo.ts @@ -5,23 +5,23 @@ import path from 'node:path' import { isPackageInstalled } from './isPackageInstalled.js' type ServiceEntry = { - name: string - version: number - path: string - serviceInfoFile: string - builderFile: string - serviceFile: string + name: string + version: number + path: string + serviceInfoFile: string + builderFile: string + serviceFile: string } type InstallInfo = { - sinonIsPresent: boolean - jestIsPresent: boolean - services: ServiceEntry[] + sinonIsPresent: boolean + jestIsPresent: boolean + services: ServiceEntry[] } export const installInfo: InstallInfo = { - sinonIsPresent: false, - jestIsPresent: false, - services: [], + sinonIsPresent: false, + jestIsPresent: false, + services: [], } const matchVersionRegex = /^\D*(\d+)$/i @@ -30,88 +30,88 @@ const builderFileRegex = /Builder\.ts$/i const serviceFileRegex = /Service\.ts$/i const getBuilderFile = (startFolder: string) => { - const files = fs.readdirSync(startFolder) - return files.find((file) => { - const match = file.match(builderFileRegex) + const files = fs.readdirSync(startFolder) + return files.find(file => { + const match = file.match(builderFileRegex) - return !!match - }) + return !!match + }) } const getServiceFile = (startFolder: string) => { - const files = fs.readdirSync(startFolder) - return files.find((file) => { - const match = file.match(serviceFileRegex) - return !!match - }) + const files = fs.readdirSync(startFolder) + return files.find(file => { + const match = file.match(serviceFileRegex) + return !!match + }) } const getServiceVersions = (startFolder: string, serviceName: string) => { - const files = fs.readdirSync(startFolder) - - const service: ServiceEntry = { - name: serviceName, - version: 0, - path: '', - serviceInfoFile: '', - builderFile: '', - serviceFile: '', - } - - files.forEach((file) => { - const name = path.join(startFolder, file) - - if (fs.statSync(name).isDirectory()) { - const versionInfo = file.match(matchVersionRegex) - if (versionInfo?.length === 2) { - service.version = parseInt(versionInfo[1]) - service.path = path.join(serviceName, path.basename(file)) - - service.builderFile = getBuilderFile(name) ?? '' - service.serviceFile = getServiceFile(name) ?? '' - } - } else { - const infoName = path.basename(file) - if (infoName.match(infoFileRegex)) { - service.serviceInfoFile = infoName - } - } - }) - - if (service.version > 0) { - installInfo.services.push(service) - } + const files = fs.readdirSync(startFolder) + + const service: ServiceEntry = { + name: serviceName, + version: 0, + path: '', + serviceInfoFile: '', + builderFile: '', + serviceFile: '', + } + + for (const file of files) { + const name = path.join(startFolder, file) + + if (fs.statSync(name).isDirectory()) { + const versionInfo = file.match(matchVersionRegex) + if (versionInfo?.length === 2) { + service.version = Number.parseInt(versionInfo[1]) + service.path = path.join(serviceName, path.basename(file)) + + service.builderFile = getBuilderFile(name) ?? '' + service.serviceFile = getServiceFile(name) ?? '' + } + } else { + const infoName = path.basename(file) + if (infoName.match(infoFileRegex)) { + service.serviceInfoFile = infoName + } + } + } + + if (service.version > 0) { + installInfo.services.push(service) + } } export const collectServices = (startFolder: string) => { - const files = fs.readdirSync(startFolder) - files.forEach((file) => { - const name = path.join(startFolder, file) - if (fs.statSync(name).isDirectory()) { - getServiceVersions(name, path.parse(file).name) - } else { - // allFilesList.push(name) // push filename into the array - } - }) - - installInfo.services.sort((a, b) => { - if (b.name < a.name) { - return 1 - } - if (b.name > a.name) { - return -1 - } - if (b.version < a.version) { - return 1 - } - if (b.version > a.version) { - return -1 - } - return 0 - }) + const files = fs.readdirSync(startFolder) + for (const file of files) { + const name = path.join(startFolder, file) + if (fs.statSync(name).isDirectory()) { + getServiceVersions(name, path.parse(file).name) + } else { + // allFilesList.push(name) // push filename into the array + } + } + + installInfo.services.sort((a, b) => { + if (b.name < a.name) { + return 1 + } + if (b.name > a.name) { + return -1 + } + if (b.version < a.version) { + return 1 + } + if (b.version > a.version) { + return -1 + } + return 0 + }) } export const collectInstallInfo = async () => { - installInfo.sinonIsPresent = await isPackageInstalled('sinon') - installInfo.jestIsPresent = await isPackageInstalled('jest') + installInfo.sinonIsPresent = await isPackageInstalled('sinon') + installInfo.jestIsPresent = await isPackageInstalled('jest') } diff --git a/packages/cli/src/helper/isPackageInstalled.ts b/packages/cli/src/helper/isPackageInstalled.ts index 23854c2bd..480bdd0cd 100644 --- a/packages/cli/src/helper/isPackageInstalled.ts +++ b/packages/cli/src/helper/isPackageInstalled.ts @@ -1,14 +1,14 @@ import { exec } from 'node:child_process' export const isPackageInstalled = async (packageName: string) => { - return new Promise((resolve) => { - const cmd = 'npm ls ' + packageName - const child = exec(cmd, (err) => { - if (err) { - resolve(false) - } - }) - child.stderr?.pipe(process.stderr) - child.on('close', () => resolve(true)) - }) + return new Promise(resolve => { + const cmd = `npm ls ${packageName}` + const child = exec(cmd, err => { + if (err) { + resolve(false) + } + }) + child.stderr?.pipe(process.stderr) + child.on('close', () => resolve(true)) + }) } diff --git a/packages/cli/src/helper/loadPackageJson.ts b/packages/cli/src/helper/loadPackageJson.ts index ad33a5f05..8ed4f5bdc 100644 --- a/packages/cli/src/helper/loadPackageJson.ts +++ b/packages/cli/src/helper/loadPackageJson.ts @@ -2,10 +2,10 @@ import { readFileSync } from 'node:fs' import { join } from 'node:path' export const loadPackageJson = (folder: string): Record => { - try { - const content = readFileSync(join(folder, 'package.json')) - return JSON.parse(content.toString('utf-8')) - } catch (error) { - throw new Error('Unable to proceed without package.json') - } + try { + const content = readFileSync(join(folder, 'package.json')) + return JSON.parse(content.toString('utf-8')) + } catch (error) { + throw new Error('Unable to proceed without package.json') + } } diff --git a/packages/cli/src/helper/registerHandlebarHelpers.ts b/packages/cli/src/helper/registerHandlebarHelpers.ts index e22aa310a..8e119a12e 100644 --- a/packages/cli/src/helper/registerHandlebarHelpers.ts +++ b/packages/cli/src/helper/registerHandlebarHelpers.ts @@ -1,8 +1,8 @@ import type { NodePlopAPI } from 'plop' export const registerHandlebarHelpers = (plop: NodePlopAPI) => { - plop.setHelper('eq', (a: string, b: string) => a === b) - plop.setHelper('includes', (a: string[], b: string) => a.includes(b)) - plop.setHelper('isSet', (a?: string) => a && a.trim().length > 0) - plop.setHelper('trim', (a?: string) => a?.trim()) + plop.setHelper('eq', (a: string, b: string) => a === b) + plop.setHelper('includes', (a: string[], b: string) => a.includes(b)) + plop.setHelper('isSet', (a?: string) => a && a.trim().length > 0) + plop.setHelper('trim', (a?: string) => a?.trim()) } diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index 63d7f2722..3432643af 100755 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -9,58 +9,43 @@ import { Plop, run } from 'plop' import { puristaVersion } from './version.js' const main = () => { - const __dirname = path.dirname(fileURLToPath(import.meta.url)) + const __dirname = path.dirname(fileURLToPath(import.meta.url)) - if (process.argv.length < 3) { - console.log('') - console.log('PURISTA cli') - console.log('') - console.log('You need to provide a action you like to perform.') - console.log('') - console.log('If you like to install PURISTA in current folder and setup a new project if needed') - console.log('') - console.log('⌨️ purista init') - console.log('') - console.log('If you like to add a ressource to existing project') - console.log('') - console.log('⌨️ purista add [service|command|subscription|version]') - console.log('') + if (process.argv.length < 3) { + process.exit() + } - process.exit() - } + let configPath = '' + if (process.argv[2] === 'init') { + configPath = path.join(__dirname, 'installation', 'plopfile.js') + } + if (process.argv[2] === 'add') { + configPath = path.join(__dirname, 'addResource', 'plopfile.js') + } - let configPath = '' - if (process.argv[2] === 'init') { - configPath = path.join(__dirname, 'installation', 'plopfile.js') - } - if (process.argv[2] === 'add') { - configPath = path.join(__dirname, 'addRessource', 'plopfile.js') - } + if (process.argv[2] === 'version') { + process.exit() + } - if (process.argv[2] === 'version') { - console.log('PURISTA CLI version', puristaVersion) - process.exit() - } + const args = process.argv.slice(3) + const argv = minimist(args) - const args = process.argv.slice(3) - const argv = minimist(args) - - Plop.prepare( - { - cwd: process.cwd(), // argv.cwd, - configPath, - preload: argv.preload || [], - completion: argv.completion, - }, - (env) => - Plop.execute(env, (env) => { - const options = { - ...env, - dest: process.cwd(), // this will make the destination path to be based on the cwd when calling the wrapper - } - return run(options, undefined, true) - }), - ) + Plop.prepare( + { + cwd: process.cwd(), // argv.cwd, + configPath, + preload: argv.preload || [], + completion: argv.completion, + }, + env => + Plop.execute(env, env => { + const options = { + ...env, + dest: process.cwd(), // this will make the destination path to be based on the cwd when calling the wrapper + } + return run(options, undefined, true) + }), + ) } const controller = new AbortController() @@ -68,20 +53,20 @@ const controller = new AbortController() const timeoutId = setTimeout(() => controller.abort(), 5000) fetch('https://registry.npmjs.org/@purista/cli/latest', { signal: controller.signal }) - .then((response) => { - clearTimeout(timeoutId) - response - .json() - .then((value: Record) => { - if (value.version !== puristaVersion) { - console.error('🚨 BE AWARE!') - console.error('Looks like your CLI version is outdated.') - console.error(`Latest version is ${value.version} - Please upgrade before you proceed!`) - console.error('') - } else { - console.log(`👍 You use latest CLI version ${value.version}`) - } - }) - .catch(console.error) - }) - .finally(main) + .then(response => { + clearTimeout(timeoutId) + response + .json() + .then((value: Record) => { + if (value.version !== puristaVersion) { + console.error('🚨 BE AWARE!') + console.error(`Looks like your CLI version is outdated. Your version is ${puristaVersion}`) + console.error(`Latest version is ${value.version} - Please upgrade before you proceed!`) + console.error('') + } else { + console.log(`👍 You use latest CLI version ${value.version}`) + } + }) + .catch(console.error) + }) + .finally(main) diff --git a/packages/cli/src/installation/initProjectActions.ts b/packages/cli/src/installation/initProjectActions.ts index 025741406..14853d89e 100644 --- a/packages/cli/src/installation/initProjectActions.ts +++ b/packages/cli/src/installation/initProjectActions.ts @@ -2,251 +2,245 @@ import type { Actions } from 'node-plop' import { - biomeDependencies, - cliDependencies, - dependencies, - devDependencies, - eslintDependencies, - httpserverDependencies, - jestDependencies, - TEMPLATE_BASE, - vitestDependencies, + TEMPLATE_BASE, + biomeDependencies, + cliDependencies, + dependencies, + devDependencies, + eslintDependencies, + httpserverDependencies, + jestDependencies, + vitestDependencies, } from '../config.js' import { installDependencies } from '../helper/installDependencies.js' export const initProjectActions: Actions = [ - (answers) => { - if (!answers.initialize) { - console.log('') - console.log('😥 I am sorry about your choice - you should try out PURISTA 😉') - console.log('') - console.log('Learn more about PURISTA at https://purista.dev') - console.log('') - process.exit() - } - return 'Installing PURISTA 🚀' - }, - { - type: 'add', - skipIfExists: true, - path: 'package.json', - templateFile: TEMPLATE_BASE + '/package.json.hbs', - }, - { - type: 'add', - skipIfExists: true, - path: 'readme.md', - templateFile: TEMPLATE_BASE + '/readme.md.hbs', - }, - { - type: 'add', - skipIfExists: true, - path: 'tsconfig.json', - templateFile: TEMPLATE_BASE + '/tsconfig.json.hbs', - }, - { - type: 'add', - skip: (answers: Record) => { - if (answers.isEsm) { - return '[SKIPPED] jest test setup' - } - }, - skipIfExists: true, - path: 'jest.config.js', - templateFile: TEMPLATE_BASE + '/jest.config.js.hbs', - }, - { - type: 'add', - skip: (answers: Record) => { - if (!answers.isEsm) { - return '[SKIPPED] vitest test setup' - } - }, - skipIfExists: true, - path: 'vite.config.ts', - templateFile: TEMPLATE_BASE + '/vite.config.ts.hbs', - }, - { - type: 'add', - skip: (answers: Record) => { - if (answers.linter !== 'eslint') { - return '[SKIPPED] lint setup' - } - }, - skipIfExists: true, - path: '.eslintignore', - templateFile: TEMPLATE_BASE + '/eslintignore.hbs', - }, - { - type: 'add', - skip: (answers: Record) => { - if (answers.linter !== 'eslint') { - return '[SKIPPED] lint setup' - } - }, - skipIfExists: true, - path: '.prettierrc', - templateFile: TEMPLATE_BASE + '/prettierrc.hbs', - }, - { - type: 'add', - skip: (answers: Record) => { - if (answers.linter !== 'eslint' || !answers.isEsm) { - return '[SKIPPED] lint setup' - } - }, - skipIfExists: true, - path: '.eslintrc.cjs', - templateFile: TEMPLATE_BASE + '/eslintrc.js.hbs', - }, - { - type: 'add', - skip: (answers: Record) => { - if (answers.linter !== 'eslint' || answers.isEsm) { - return '[SKIPPED] lint setup' - } - }, - skipIfExists: true, - path: '.eslintrc.js', - templateFile: TEMPLATE_BASE + '/eslintrc.js.hbs', - }, - { - type: 'add', - skip: (answers: Record) => { - if (answers.linter !== 'biome') { - return '[SKIPPED] biome setup' - } - }, - skipIfExists: true, - path: 'biome.json', - templateFile: TEMPLATE_BASE + '/biome.json.hbs', - }, - { - type: 'add', - skip: (answers: Record) => { - if (answers.eventBridge !== 'AmqpEventBridge') { - return '[SKIPPED] AMQP event bridge config' - } - }, - skipIfExists: true, - path: 'config/amqpBridgeConfig.ts', - templateFile: TEMPLATE_BASE + '/config/amqpBridgeConfig.ts.hbs', - }, - { - type: 'add', - skip: (answers: Record) => { - if (answers.eventBridge !== 'MqttEventBridge') { - return '[SKIPPED] MQTT event bridge config' - } - }, - skipIfExists: true, - path: 'config/mqttBridgeConfig.ts', - templateFile: TEMPLATE_BASE + '/config/mqttBridgeConfig.ts.hbs', - }, - { - type: 'add', - skip: (answers: Record) => { - if (answers.eventBridge !== 'NatsEventBridge') { - return '[SKIPPED] NATS event bridge config' - } - }, - skipIfExists: true, - path: 'config/natsBridgeConfig.ts', - templateFile: TEMPLATE_BASE + '/config/natsBridgeConfig.ts.hbs', - }, - { - type: 'add', - skip: (answers: Record) => { - if (answers.eventBridge === 'DaprEventBridge') { - return '[SKIPPED] index files must be created for each service individual' - } - }, - skipIfExists: true, - path: 'src/index.ts', - templateFile: TEMPLATE_BASE + '/src/index.ts.hbs', - }, - { - type: 'add', - skip: (answers: Record) => { - if (!answers.installHttpService) { - return '[SKIPPED] http server static install' - } - }, - skipIfExists: true, - path: 'public/index.html', - templateFile: TEMPLATE_BASE + '/public/index.html.hbs', - }, - async (answers) => { - console.log('Installing packages - please wait') - const deps = dependencies + answers => { + if (!answers.initialize) { + process.exit() + } + return 'Installing PURISTA 🚀' + }, + { + type: 'add', + skipIfExists: true, + path: 'package.json', + templateFile: `${TEMPLATE_BASE}/package.json.hbs`, + }, + { + type: 'add', + skipIfExists: true, + path: 'readme.md', + templateFile: `${TEMPLATE_BASE}/readme.md.hbs`, + }, + { + type: 'add', + skipIfExists: true, + path: 'tsconfig.json', + templateFile: `${TEMPLATE_BASE}/tsconfig.json.hbs`, + }, + { + type: 'add', + skip: (answers: Record) => { + if (answers.isEsm) { + return '[SKIPPED] jest test setup' + } + }, + skipIfExists: true, + path: 'jest.config.js', + templateFile: `${TEMPLATE_BASE}/jest.config.js.hbs`, + }, + { + type: 'add', + skip: (answers: Record) => { + if (!answers.isEsm) { + return '[SKIPPED] vitest test setup' + } + }, + skipIfExists: true, + path: 'vite.config.ts', + templateFile: `${TEMPLATE_BASE}/vite.config.ts.hbs`, + }, + { + type: 'add', + skip: (answers: Record) => { + if (answers.linter !== 'eslint') { + return '[SKIPPED] lint setup' + } + }, + skipIfExists: true, + path: '.eslintignore', + templateFile: `${TEMPLATE_BASE}/eslintignore.hbs`, + }, + { + type: 'add', + skip: (answers: Record) => { + if (answers.linter !== 'eslint') { + return '[SKIPPED] lint setup' + } + }, + skipIfExists: true, + path: '.prettierrc', + templateFile: `${TEMPLATE_BASE}/prettierrc.hbs`, + }, + { + type: 'add', + skip: (answers: Record) => { + if (answers.linter !== 'eslint' || !answers.isEsm) { + return '[SKIPPED] lint setup' + } + }, + skipIfExists: true, + path: '.eslintrc.cjs', + templateFile: `${TEMPLATE_BASE}/eslintrc.js.hbs`, + }, + { + type: 'add', + skip: (answers: Record) => { + if (answers.linter !== 'eslint' || answers.isEsm) { + return '[SKIPPED] lint setup' + } + }, + skipIfExists: true, + path: '.eslintrc.js', + templateFile: `${TEMPLATE_BASE}/eslintrc.js.hbs`, + }, + { + type: 'add', + skip: (answers: Record) => { + if (answers.linter !== 'biome') { + return '[SKIPPED] biome setup' + } + }, + skipIfExists: true, + path: 'biome.json', + templateFile: `${TEMPLATE_BASE}/biome.json.hbs`, + }, + { + type: 'add', + skip: (answers: Record) => { + if (answers.eventBridge !== 'AmqpEventBridge') { + return '[SKIPPED] AMQP event bridge config' + } + }, + skipIfExists: true, + path: 'config/amqpBridgeConfig.ts', + templateFile: `${TEMPLATE_BASE}/config/amqpBridgeConfig.ts.hbs`, + }, + { + type: 'add', + skip: (answers: Record) => { + if (answers.eventBridge !== 'MqttEventBridge') { + return '[SKIPPED] MQTT event bridge config' + } + }, + skipIfExists: true, + path: 'config/mqttBridgeConfig.ts', + templateFile: `${TEMPLATE_BASE}/config/mqttBridgeConfig.ts.hbs`, + }, + { + type: 'add', + skip: (answers: Record) => { + if (answers.eventBridge !== 'NatsEventBridge') { + return '[SKIPPED] NATS event bridge config' + } + }, + skipIfExists: true, + path: 'config/natsBridgeConfig.ts', + templateFile: `${TEMPLATE_BASE}/config/natsBridgeConfig.ts.hbs`, + }, + { + type: 'add', + skip: (answers: Record) => { + if (answers.eventBridge === 'DaprEventBridge') { + return '[SKIPPED] index files must be created for each service individual' + } + }, + skipIfExists: true, + path: 'src/index.ts', + templateFile: `${TEMPLATE_BASE}/src/index.ts.hbs`, + }, + { + type: 'add', + skip: (answers: Record) => { + if (!answers.installHttpService) { + return '[SKIPPED] http server static install' + } + }, + skipIfExists: true, + path: 'public/index.html', + templateFile: `${TEMPLATE_BASE}/public/index.html.hbs`, + }, + async answers => { + const deps = dependencies - if (answers.installHttpService) { - deps.push(...httpserverDependencies) - } + if (answers.installHttpService) { + deps.push(...httpserverDependencies) + } - switch (answers.eventBridge) { - case 'AmqpEventBridge': - deps.push('@purista/amqpbridge') - break - case 'MqttEventBridge': - deps.push('@purista/mqttbridge') - break - case 'NatsEventBridge': - deps.push('@purista/natsbridge') - break - case 'DaprEventBridge': - deps.push('@purista/dapr-sdk') - break - } + switch (answers.eventBridge) { + case 'AmqpEventBridge': + deps.push('@purista/amqpbridge') + break + case 'MqttEventBridge': + deps.push('@purista/mqttbridge') + break + case 'NatsEventBridge': + deps.push('@purista/natsbridge') + break + case 'DaprEventBridge': + deps.push('@purista/dapr-sdk') + break + } - await installDependencies('npm install --save-prod ' + deps.join(' ')) + await installDependencies(`npm install --save-prod ${deps.join(' ')}`) - const devDeps = devDependencies + const devDeps = devDependencies - if (answers.installCliGlobal === 'local') { - devDeps.push(...cliDependencies) - } + if (answers.installCliGlobal === 'local') { + devDeps.push(...cliDependencies) + } - if (!answers.isEsm) { - devDeps.push(...jestDependencies) - } else { - devDeps.push(...vitestDependencies) - } + if (!answers.isEsm) { + devDeps.push(...jestDependencies) + } else { + devDeps.push(...vitestDependencies) + } - if (answers.linter === 'eslint') { - if (answers.isEsm) { - devDeps.push('eslint-plugin-vitest') - } - devDeps.push(...eslintDependencies) - } + if (answers.linter === 'eslint') { + if (answers.isEsm) { + devDeps.push('eslint-plugin-vitest') + } + devDeps.push(...eslintDependencies) + } - if (answers.linter === 'biome') { - devDeps.push(...biomeDependencies) - } + if (answers.linter === 'biome') { + devDeps.push(...biomeDependencies) + } - await installDependencies('npm install --save-dev ' + devDeps.join(' ')) + await installDependencies(`npm install --save-dev ${devDeps.join(' ')}`) - if (answers.installCliGlobal === 'global') { - await installDependencies('npm install -g ' + cliDependencies.join(' ')) - } + if (answers.installCliGlobal === 'global') { + await installDependencies(`npm install -g ${cliDependencies.join(' ')}`) + } - return 'needed packages installed' - }, - (answers) => { - console.log('') - console.log('🎉 SUCCESS - PURISTA project ready 🎉') - console.log('Enjoy building awesome applications with PURISTA 🚀') - console.log('') - if (answers.eventBridge === 'DaprEventBridge') { - console.log('🚨 As you are using the Dapr event bridge you might need to install additional packages!') - console.log('🚨 You also need to setup the config for your runtime environment.') - console.log('🚨 see https://purista.dev/handbook/3._event-bridge/5_dapr.html') - } - console.log('') - console.log('Now it is time to add your first service!') - console.log('') - console.log('➡️ purista add service') - console.log('') - return '📖 Learn more about PURISTA at https://purista.dev' - }, + return 'needed packages installed' + }, + answers => { + console.log('') + console.log('🎉 SUCCESS - PURISTA project ready 🎉') + console.log('Enjoy building awesome applications with PURISTA 🚀') + console.log('') + if (answers.eventBridge === 'DaprEventBridge') { + console.log('🚨 As you are using the Dapr event bridge you might need to install additional packages!') + console.log('🚨 You also need to setup the config for your runtime environment.') + console.log('🚨 see https://purista.dev/handbook/3._event-bridge/5_dapr.html') + } + console.log('') + console.log('Now it is time to add your first service!') + console.log('') + console.log('➡️ purista add service') + console.log('') + return '📖 Learn more about PURISTA at https://purista.dev' + }, ] diff --git a/packages/cli/src/installation/initProjectPrompts.ts b/packages/cli/src/installation/initProjectPrompts.ts index f167a1e70..2a499b637 100644 --- a/packages/cli/src/installation/initProjectPrompts.ts +++ b/packages/cli/src/installation/initProjectPrompts.ts @@ -2,78 +2,78 @@ import type { Prompts } from 'node-plop' export const initProjectPrompts: Prompts = [ - { - type: 'list', - message: 'What do you want to do?', - name: 'intention', - choices: [ - { value: 'add', name: 'add ressource' }, - { value: 'init', name: 'init PURISTA' }, - ], - }, - { - type: 'confirm', - message: 'Initialize PURISTA in current folder', - name: 'initialize', - }, - { - type: 'list', - message: 'What do you want to do?', - name: 'isEsm', - choices: [ - { value: true, name: 'esm module' }, - { value: false, name: 'commonjs' }, - ], - when(answers) { - return answers.initialize - }, - }, - { - type: 'list', - name: 'installCliGlobal', - message: 'Install PURISTA cli globally?', - when(answers) { - return answers.initialize - }, - choices: [ - { name: 'install as global npm module', value: 'global', checked: true }, - { name: 'as local module in this project only', value: 'local' }, - { name: 'no install', value: 'none' }, - ], - }, - { - type: 'list', - name: 'linter', - message: 'Choose code prettier & linter', - when(answers) { - return answers.initialize - }, - choices: [ - { name: 'ESlint & prettier', value: 'eslint', checked: true }, - { name: 'Biome', value: 'biome' }, - ], - }, - { - type: 'list', - message: 'Which messaging system should be used', - name: 'eventBridge', - when(answers) { - return answers.initialize - }, - choices: [ - { value: 'DefaultEventBridge', name: 'Default In-Memory', checked: true }, - { value: 'AmqpEventBridge', name: 'AMQP eventbridge (RabbitMQ)' }, - { value: 'MqttEventBridge', name: 'MQTT eventbridge (mosquitto)' }, - { value: 'NatsEventBridge', name: 'NATS eventbridge' }, - { value: 'DaprEventBridge', name: 'Dapr eventbridge' }, - ], - }, - { - type: 'confirm', - message: 'Should the @purista/httpserver package be installed, to automatically provide a REST api server?', - name: 'installHttpService', - when(answers) { - return answers.initialize && answers.eventBridge !== 'DaprEventBridge' - }, - }, + { + type: 'list', + message: 'What do you want to do?', + name: 'intention', + choices: [ + { value: 'add', name: 'add resource' }, + { value: 'init', name: 'init PURISTA' }, + ], + }, + { + type: 'confirm', + message: 'Initialize PURISTA in current folder', + name: 'initialize', + }, + { + type: 'list', + message: 'What do you want to do?', + name: 'isEsm', + choices: [ + { value: true, name: 'esm module' }, + { value: false, name: 'commonjs' }, + ], + when(answers) { + return answers.initialize + }, + }, + { + type: 'list', + name: 'installCliGlobal', + message: 'Install PURISTA cli globally?', + when(answers) { + return answers.initialize + }, + choices: [ + { name: 'install as global npm module', value: 'global', checked: true }, + { name: 'as local module in this project only', value: 'local' }, + { name: 'no install', value: 'none' }, + ], + }, + { + type: 'list', + name: 'linter', + message: 'Choose code prettier & linter', + when(answers) { + return answers.initialize + }, + choices: [ + { name: 'ESlint & prettier', value: 'eslint', checked: true }, + { name: 'Biome', value: 'biome' }, + ], + }, + { + type: 'list', + message: 'Which messaging system should be used', + name: 'eventBridge', + when(answers) { + return answers.initialize + }, + choices: [ + { value: 'DefaultEventBridge', name: 'Default In-Memory', checked: true }, + { value: 'AmqpEventBridge', name: 'AMQP eventbridge (RabbitMQ)' }, + { value: 'MqttEventBridge', name: 'MQTT eventbridge (mosquitto)' }, + { value: 'NatsEventBridge', name: 'NATS eventbridge' }, + { value: 'DaprEventBridge', name: 'Dapr eventbridge' }, + ], + }, + { + type: 'confirm', + message: 'Should the @purista/httpserver package be installed, to automatically provide a REST api server?', + name: 'installHttpService', + when(answers) { + return answers.initialize && answers.eventBridge !== 'DaprEventBridge' + }, + }, ] diff --git a/packages/cli/src/installation/plopfile.ts b/packages/cli/src/installation/plopfile.ts index 1c495ba60..93cde18f9 100644 --- a/packages/cli/src/installation/plopfile.ts +++ b/packages/cli/src/installation/plopfile.ts @@ -5,13 +5,13 @@ import { initProjectActions } from './initProjectActions.js' import { initProjectPrompts } from './initProjectPrompts.js' export default function (plop: NodePlopAPI) { - registerHandlebarHelpers(plop) + registerHandlebarHelpers(plop) - plop.setWelcomeMessage('Welcome to PURISTA CLI') + plop.setWelcomeMessage('Welcome to PURISTA CLI') - plop.setGenerator('rootMenu', { - description: 'Init a new project or add PURISTA to current project', - prompts: initProjectPrompts, - actions: initProjectActions, - }) + plop.setGenerator('rootMenu', { + description: 'Init a new project or add PURISTA to current project', + prompts: initProjectPrompts, + actions: initProjectActions, + }) } diff --git a/packages/cli/src/manipulation/addDefinitionToBuilder.ts b/packages/cli/src/manipulation/addDefinitionToBuilder.ts index 875501236..c799934b3 100644 --- a/packages/cli/src/manipulation/addDefinitionToBuilder.ts +++ b/packages/cli/src/manipulation/addDefinitionToBuilder.ts @@ -2,38 +2,34 @@ import { Project, SyntaxKind } from 'ts-morph' export const addDefinitionToBuilder = ( - arrayName: 'commandDefinitions' | 'subscriptionDefinitions', - serviceBuilderFile: string, - importFile: string, - importDefinition: string, + arrayName: 'commandDefinitions' | 'subscriptionDefinitions', + serviceBuilderFile: string, + importFile: string, + importDefinition: string, ) => { - console.log('👷🏗️ -> try to add definition to builder') + const project = new Project({ + tsConfigFilePath: './tsconfig.json', + }) - const project = new Project({ - tsConfigFilePath: './tsconfig.json', - }) + project.addSourceFilesAtPaths('**/*.ts') - project.addSourceFilesAtPaths('**/*.ts') + const sourceFile = project.getSourceFileOrThrow(serviceBuilderFile) - const sourceFile = project.getSourceFileOrThrow(serviceBuilderFile) + sourceFile.addImportDeclaration({ + namedImports: [importDefinition], + moduleSpecifier: importFile, + }) - sourceFile.addImportDeclaration({ - namedImports: [importDefinition], - moduleSpecifier: importFile, - }) + const arrayDeclaration = sourceFile.getVariableDeclaration(arrayName) + if (!arrayDeclaration) { + return + } - const arrayDeclaration = sourceFile.getVariableDeclaration(arrayName) - if (!arrayDeclaration) { - return console.log('⚠️ could not find arrayDeclaration ' + arrayName + ' ⚠️') - } + const arrayLiteralExpression = arrayDeclaration.getInitializerIfKind(SyntaxKind.ArrayLiteralExpression) + if (!arrayLiteralExpression) { + return + } + arrayLiteralExpression.addElement(`${importDefinition}.getDefinition()`) - const arrayLiteralExpression = arrayDeclaration.getInitializerIfKind(SyntaxKind.ArrayLiteralExpression) - if (!arrayLiteralExpression) { - return console.log('⚠️ could not find arrayLiteralExpression ' + arrayName + ' ⚠️') - } - arrayLiteralExpression.addElement(importDefinition + '.getDefinition()') - - console.log('👍 -> definition added to service builder') - - return sourceFile.save() + return sourceFile.save() } diff --git a/packages/cli/src/manipulation/addEventEnumToCommandBuilder.ts b/packages/cli/src/manipulation/addEventEnumToCommandBuilder.ts index ee41c0f16..41be06578 100644 --- a/packages/cli/src/manipulation/addEventEnumToCommandBuilder.ts +++ b/packages/cli/src/manipulation/addEventEnumToCommandBuilder.ts @@ -2,40 +2,36 @@ import { Project } from 'ts-morph' export const addEventEnumToCommandBuilder = async (file: string, enumName?: string) => { - if (!enumName) { - return console.log('skip set event name from enum in command builder') - } + if (!enumName) { + return + } - console.log('👷🏗️ -> set event name from enum in command builder') + const project = new Project({ + tsConfigFilePath: './tsconfig.json', + }) - const project = new Project({ - tsConfigFilePath: './tsconfig.json', - }) + project.addSourceFilesAtPaths('**/*.ts') - project.addSourceFilesAtPaths('**/*.ts') + const sourceFile = project.getSourceFileOrThrow(file) - const sourceFile = project.getSourceFileOrThrow(file) + const builder = sourceFile + .getVariableDeclarations() + .find(declaration => declaration.getText().includes('setSuccessEventName')) - const builder = sourceFile - .getVariableDeclarations() - .find((declaration) => declaration.getText().includes('setSuccessEventName')) + if (!builder) { + return + } - if (!builder) { - return console.log('⚠️ could not find command builder call ⚠️') - } + const newText = builder + .getText() + .replace(/\.setSuccessEventName\((["'].*["'])\)/gm, `.setSuccessEventName(ServiceEvent.${enumName})`) - const newText = builder - .getText() - .replace(/\.setSuccessEventName\((["'].*["'])\)/gm, `.setSuccessEventName(ServiceEvent.${enumName})`) + builder.replaceWithText(newText) - builder.replaceWithText(newText) + sourceFile.addImportDeclaration({ + namedImports: ['ServiceEvent'], + moduleSpecifier: '../../../../ServiceEvent.enum.js', + }) - sourceFile.addImportDeclaration({ - namedImports: ['ServiceEvent'], - moduleSpecifier: '../../../../ServiceEvent.enum.js', - }) - - console.log('👍 -> updated event to use enum in command builder') - - return sourceFile.save() + return sourceFile.save() } diff --git a/packages/cli/src/manipulation/addEventEnumToSubscriptionBuilder.ts b/packages/cli/src/manipulation/addEventEnumToSubscriptionBuilder.ts index 3db248eba..dd6f9db43 100644 --- a/packages/cli/src/manipulation/addEventEnumToSubscriptionBuilder.ts +++ b/packages/cli/src/manipulation/addEventEnumToSubscriptionBuilder.ts @@ -2,40 +2,36 @@ import { Project } from 'ts-morph' export const addEventEnumToSubscriptionBuilder = async (file: string, enumName?: string) => { - if (!enumName) { - return console.log('skip set event name from enum in subscription builder') - } + if (!enumName) { + return + } - console.log('👷🏗️ -> set event name from enum in subscription builder') + const project = new Project({ + tsConfigFilePath: './tsconfig.json', + }) - const project = new Project({ - tsConfigFilePath: './tsconfig.json', - }) + project.addSourceFilesAtPaths('**/*.ts') - project.addSourceFilesAtPaths('**/*.ts') + const sourceFile = project.getSourceFileOrThrow(file) - const sourceFile = project.getSourceFileOrThrow(file) + const builder = sourceFile + .getVariableDeclarations() + .find(declaration => declaration.getText().includes('subscribeToEvent')) - const builder = sourceFile - .getVariableDeclarations() - .find((declaration) => declaration.getText().includes('subscribeToEvent')) + if (!builder) { + return + } - if (!builder) { - return console.log('⚠️ could not find subscription builder call ⚠️') - } + const newText = builder + .getText() + .replace(/\.subscribeToEvent\((["'].*["'])\)/gm, `.subscribeToEvent(ServiceEvent.${enumName})`) - const newText = builder - .getText() - .replace(/\.subscribeToEvent\((["'].*["'])\)/gm, `.subscribeToEvent(ServiceEvent.${enumName})`) + builder.replaceWithText(newText) - builder.replaceWithText(newText) + sourceFile.addImportDeclaration({ + namedImports: ['ServiceEvent'], + moduleSpecifier: '../../../../ServiceEvent.enum.js', + }) - sourceFile.addImportDeclaration({ - namedImports: ['ServiceEvent'], - moduleSpecifier: '../../../../ServiceEvent.enum.js', - }) - - console.log('👍 -> updated event to use enum in subscription builder') - - return sourceFile.save() + return sourceFile.save() } diff --git a/packages/cli/src/manipulation/ensureServiceEvent.ts b/packages/cli/src/manipulation/ensureServiceEvent.ts index d66779842..bb207d989 100644 --- a/packages/cli/src/manipulation/ensureServiceEvent.ts +++ b/packages/cli/src/manipulation/ensureServiceEvent.ts @@ -5,53 +5,45 @@ import camelCase from 'camelcase' import { Project } from 'ts-morph' export const ensureServiceEvent = async (eventName: string | undefined, description?: string) => { - if (!eventName?.trim().length) { - console.log('skip - no event name to add') - return - } + if (!eventName?.trim().length) { + return + } - console.log('👷🏗️ -> ensure new enum entry') + const tsConfigFilePath = join(process.cwd(), 'tsconfig.json') + const project = new Project({ + tsConfigFilePath, + }) - const tsConfigFilePath = join(process.cwd(), 'tsconfig.json') - const project = new Project({ - tsConfigFilePath, - }) + const enumFile = join('.', 'src', 'service', 'ServiceEvent.enum.ts') - const enumFile = join('.', 'src', 'service', 'ServiceEvent.enum.ts') + const sourceFile = project.addSourceFileAtPathIfExists(enumFile) - const sourceFile = project.addSourceFileAtPathIfExists(enumFile) + if (!sourceFile) { + throw new Error(`${enumFile} could not be found`) + } - if (!sourceFile) { - throw new Error(enumFile + ' could not be found') - } + const serviceEventEnum = sourceFile.getEnum('ServiceEvent') - const serviceEventEnum = sourceFile.getEnum('ServiceEvent') + if (!serviceEventEnum) { + throw new Error('enum ServiceEvent could not be found') + } - if (!serviceEventEnum) { - throw new Error('enum ServiceEvent could not be found') - } + const enumValue = eventName.trim() + const enumName = camelCase(enumValue, { pascalCase: true, preserveConsecutiveUppercase: true }) - const enumValue = eventName.trim() - const enumName = camelCase(enumValue, { pascalCase: true, preserveConsecutiveUppercase: true }) + const existingEntries = serviceEventEnum.getMembers() - const existingEntries = serviceEventEnum.getMembers() + const alreadyExist = existingEntries.find(member => member.getName() === enumName || member.getValue() === enumValue) - const alreadyExist = existingEntries.find( - (member) => member.getName() === enumName || member.getValue() === enumValue, - ) + if (alreadyExist) { + return alreadyExist.getName() + } - if (alreadyExist) { - console.log('🕵️ -> event exist in enum', enumName) - return alreadyExist.getName() - } + const member = serviceEventEnum.addMember({ name: enumName, value: enumValue }) + if (description) { + member.addJsDoc(description) + } - const member = serviceEventEnum.addMember({ name: enumName, value: enumValue }) - if (description) { - member.addJsDoc(description) - } - - await sourceFile.save() - - console.log(`👍 -> event "${enumValue}" added to enum as ServiceEvent.${enumName}`) - return enumName + await sourceFile.save() + return enumName } diff --git a/packages/cli/src/manipulation/lintFiles.ts b/packages/cli/src/manipulation/lintFiles.ts index fb01ca21f..aa41c2487 100644 --- a/packages/cli/src/manipulation/lintFiles.ts +++ b/packages/cli/src/manipulation/lintFiles.ts @@ -1,14 +1,14 @@ import { exec } from 'node:child_process' export const lintFiles = async (_files: string[]) => { - const cmd = 'npm run lint:fix' + const cmd = 'npm run lint:fix' - const child = exec(cmd, (err) => { - if (err) { - throw err - } - }) - child.stderr?.pipe(process.stderr) - child.stdout?.pipe(process.stdout) - return new Promise((resolve) => child.on('close', resolve)) + const child = exec(cmd, err => { + if (err) { + throw err + } + }) + child.stderr?.pipe(process.stderr) + child.stdout?.pipe(process.stdout) + return new Promise(resolve => child.on('close', resolve)) } diff --git a/packages/cli/src/types/MainOptions.enum.ts b/packages/cli/src/types/MainOptions.enum.ts index 959286afd..d3b3b6051 100644 --- a/packages/cli/src/types/MainOptions.enum.ts +++ b/packages/cli/src/types/MainOptions.enum.ts @@ -1,4 +1,4 @@ export enum MainOption { - InitProject = 'init', - AddSomething = 'add', + InitProject = 'init', + AddSomething = 'add', } diff --git a/packages/cli/tsconfig.json b/packages/cli/tsconfig.json index 9eac969ce..a246c7e7d 100644 --- a/packages/cli/tsconfig.json +++ b/packages/cli/tsconfig.json @@ -1,17 +1,15 @@ { - "compilerOptions": { - // Required - "module": "ESNext", - "esModuleInterop": true, - "skipLibCheck": true, - "moduleResolution": "node", - // Not required - "strict": true, - "baseUrl": "./", - "paths": { - "plop": ["../../src/plop.d.ts"] - }, - "outDir": "./dist" - }, - "include": ["./src","./blueprint"] -} \ No newline at end of file + "compilerOptions": { + "module": "ESNext", + "esModuleInterop": true, + "skipLibCheck": true, + "moduleResolution": "node", + "strict": true, + "baseUrl": "./", + "paths": { + "plop": ["../../src/plop.d.ts"] + }, + "outDir": "./dist" + }, + "include": ["./src", "./blueprint"] +} diff --git a/packages/core/.gitignore b/packages/core/.gitignore new file mode 100644 index 000000000..a3101bceb --- /dev/null +++ b/packages/core/.gitignore @@ -0,0 +1 @@ +test/tmp/**/ \ No newline at end of file diff --git a/packages/core/jsr.json b/packages/core/jsr.json new file mode 100644 index 000000000..b01b29eae --- /dev/null +++ b/packages/core/jsr.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://jsr.io/schema/config-file.v1.json", + "name": "@purista/core", + "version": "1.11.0", + "description": "purista backend framework", + "keywords": ["purista", "framework", "typescript", "javascript"], + "exports": "./dist/esm/index.js", + "publish": { + "include": ["dist/**/*.js", "dist/**/*.d.ts", "README.md", "package.json"], + "exclude": [ + "src", + ".github", + ".vscode", + ".zed", + "!dist", + "!dist/**/*.js", + "!dist/**/*.d.ts", + ".tshy", + ".tshy-build", + "vendor", + "docs", + "typedoc.json", + "..eslintcache", + ".npmignore" + ] + } +} diff --git a/packages/core/package.json b/packages/core/package.json index d939b342f..b92c6d7bb 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,84 +1,94 @@ { - "name": "@purista/core", - "version": "1.11.0", - "description": "purista backend framework", - "homepage": "https://purista.dev", - "repository": { - "type": "git", - "url": "git@github.com:sebastianwessel/purista.git" - }, - "author": "Sebastian Wessel", - "license": "ISC", - "type": "module", - "files": [ - "dist" - ], - "publishConfig": { - "access": "public" - }, - "engines": { - "node": ">=18.15" - }, - "scripts": { - "lint": "eslint . --ext .ts,.json --cache . --fix", - "test": "vitest", - "build": "rimraf dist && tshy" - }, - "tshy": { - "exclude": [ - "src/**/*.test.ts" - ], - "exports": { - "./package.json": "./package.json", - ".": "./src/index.ts" - } - }, - "devDependencies": { - "@sodaru/yup-to-json-schema": "^2.0.1", - "@types/node": "^20.11.16", - "@typeschema/yup": "^0.13.1", - "sinon": "^17.0.1", - "tshy": "^1.11.1", - "yup": "^1.3.3" - }, - "dependencies": { - "@opentelemetry/api": "^1.7.0", - "@opentelemetry/resources": "^1.19.0", - "@opentelemetry/sdk-trace-node": "^1.19.0", - "@opentelemetry/semantic-conventions": "^1.19.0", - "@typeschema/main": "^0.13.1", - "@typeschema/zod": "^0.13.1", - "openapi3-ts": "^4.2.1", - "pino": "^8.19.0", - "ts-deepmerge": "^7.0.0", - "zod": "^3.22.4", - "zod-to-json-schema": "^3.22.4" - }, - "peerDependencies": { - "@types/sinon": "^17.0.3", - "sinon": "^17.x" - }, - "peerDependenciesMeta": { - "@types/sinon": { - "optional": true - }, - "sinon": { - "optional": true - } - }, - "exports": { - "./package.json": "./package.json", - ".": { - "import": { - "types": "./dist/esm/index.d.ts", - "default": "./dist/esm/index.js" - }, - "require": { - "types": "./dist/commonjs/index.d.ts", - "default": "./dist/commonjs/index.js" - } - } - }, - "main": "./dist/commonjs/index.js", - "types": "./dist/commonjs/index.d.ts" + "name": "@purista/core", + "version": "1.11.0", + "description": "purista backend framework", + "homepage": "https://purista.dev", + "repository": { + "type": "git", + "url": "git@github.com:puristajs/purista.git" + }, + "author": "Sebastian Wessel", + "license": "ISC", + "type": "module", + "files": ["dist"], + "publishConfig": { + "access": "public" + }, + "engines": { + "node": ">=18.15" + }, + "scripts": { + "lint": "npx @biomejs/biome check --write", + "test": "tsc --noEmit && vitest -c ../../vitest.config.ts", + "build": "rimraf dist && tshy", + "test:integration": "vitest -c ../../vitest.config.integration.ts" + }, + "tshy": { + "exclude": ["src/**/*.test.ts"], + "exports": { + "./package.json": "./package.json", + ".": "./src/index.ts" + } + }, + "devDependencies": { + "@sodaru/yup-to-json-schema": "^2.0.1", + "@types/node": "^22.5.1", + "@typeschema/yup": "^0.14.0", + "code-block-writer": "^13.0.2", + "rimraf": "^6.0.1", + "sinon": "^19.0.2", + "tshy": "^3.0.2", + "vitest": "^3.0.4", + "yup": "^1.4.0" + }, + "dependencies": { + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/resources": "^1.26.0", + "@opentelemetry/sdk-trace-node": "^1.26.0", + "@opentelemetry/semantic-conventions": "^1.27.0", + "@typeschema/json": "^0.14.0", + "@typeschema/main": "^0.14.0", + "@typeschema/zod": "^0.14.0", + "openapi3-ts": "^4.4.0", + "pino": "^9.2.0", + "ts-deepmerge": "^7.0.0", + "zod": "^3.24.1", + "zod-to-json-schema": "^3.24.1" + }, + "peerDependencies": { + "@types/sinon": "^17.0.3", + "code-block-writer": "^13.x", + "rimraf": "^5.x", + "sinon": "^17.x" + }, + "peerDependenciesMeta": { + "@types/sinon": { + "optional": true + }, + "sinon": { + "optional": true + }, + "rimraf": { + "optional": true + }, + "code-block-writer": { + "optional": true + } + }, + "exports": { + "./package.json": "./package.json", + ".": { + "import": { + "types": "./dist/esm/index.d.ts", + "default": "./dist/esm/index.js" + }, + "require": { + "types": "./dist/commonjs/index.d.ts", + "default": "./dist/commonjs/index.js" + } + } + }, + "main": "./dist/commonjs/index.js", + "types": "./dist/commonjs/index.d.ts", + "module": "./dist/esm/index.js" } diff --git a/packages/core/src/ClientBuilder/ClientBuilder.impl.ts b/packages/core/src/ClientBuilder/ClientBuilder.impl.ts new file mode 100644 index 000000000..c385cb3e3 --- /dev/null +++ b/packages/core/src/ClientBuilder/ClientBuilder.impl.ts @@ -0,0 +1,779 @@ +import type { WriteStream } from 'node:fs' +import { createWriteStream } from 'node:fs' +import { mkdir, readFile, readdir, writeFile } from 'node:fs/promises' +import { join } from 'node:path' + +import { rimraf } from 'rimraf' +import ts from 'typescript' + +import type { HttpExposedServiceMeta } from '../core/index.js' +import { GenericEventEmitter, isHttpExposedServiceMeta } from '../core/index.js' +import type { FullDefinition, FullServiceDefinition } from '../helper/index.js' +import { convertToCamelCase } from '../helper/index.js' +import { puristaVersion } from '../version.js' +import { getWriter } from './getWriter.impl.js' +import { mergeIntoServiceDefintion } from './mergeIntoServiceDefintion.impl.js' +import { metaToFunctionBridge } from './metaToFunctionBridge.impl.js' +import { metaToFunctionHttp } from './metaToFunctionHttp.impl.js' +import { configFullSchema, configSchema } from './schema/configSchema.js' +import type { ClientBuilderEvents } from './types/ClientBuilderEvents.js' +import type { Config, ConfigFull } from './types/Config.js' + +export const CONFIG_FILE_NAME = 'purista.client.json' + +/** + * ClientBuilder to generate clients, based on service definitions. + */ +export class ClientBuilder extends GenericEventEmitter { + public config: ConfigFull + + /** + * The root file from where the relative paths are resolved. + * Defaults to current users directory + */ + public rootPath = process.cwd() + + constructor(config?: Partial) { + super() + this.config = configFullSchema.parse({ + version: puristaVersion, + definitionPath: './definitions', + outputPath: './dist', + buildAs: 'both', + ...config, + httpClient: { + clientName: 'HttpClient', + ...config?.httpClient, + }, + eventBridgeClient: { + clientName: 'EventBridgeClient', + ...config?.eventBridgeClient, + }, + package: { + name: 'my-custom-client-package', + description: 'The client library package for a PURISTA based application', + private: true, + ...config?.package, + }, + }) + } + + /** + * Loads the config fom JSON file. + * If no path is provided, it will try to load the config from purista.client.json in rootPath directory + * @param path + */ + async loadConfig(path?: string) { + const p = path ?? join(this.rootPath, CONFIG_FILE_NAME) + const content = await readFile(p) + const parsedContent = JSON.parse(content.toString('utf-8')) + const config = configSchema.parse(parsedContent) + + this.config = configFullSchema.parse({ + ...config, + httpClient: { + buildAs: 'esm', + clientName: 'HttpClient', + ...config?.httpClient, + }, + eventBridgeClient: { + ...config?.eventBridgeClient, + }, + }) + } + + /** + * Writes the config to a config file. + * Defaults to purista.client.json in rootPath directory + * + * @param path + */ + async writeConfig(path?: string) { + const p = path ?? join(this.rootPath, CONFIG_FILE_NAME) + await writeFile(p, JSON.stringify(this.config, null, 2)) + } + + /** + * Resolves the definitions folder path from config with rootPath + * @returns path of definitions folder + */ + getDefinitionPath() { + return join(this.rootPath, this.config.definitionPath) + } + + /** + * Resolves the output folder path from config with rootPath + * @returns path of output folder + */ + getOutputPath() { + return join(this.rootPath, this.config.outputPath) + } + + /** + * Deletes the content of the output folder. + * Should be called before generating the client + * @returns + */ + async cleanDistFolder() { + await rimraf(this.getOutputPath()) + await mkdir(this.getOutputPath()) + await mkdir(join(this.getOutputPath(), 'src')) + } + + /** + * Creates a index.ts file which exports the client(s) and types. + * Is used in generated package.json + */ + async createIndex() { + const ext = this.config.buildAs !== 'commonjs' ? '.js' : '' + + const writer = getWriter() + + const path = join(this.getOutputPath(), 'src') + + const files = await readdir(path) + for (const file of files) { + if (!file.endsWith('.ts')) { + continue + } + writer.writeLine(`export * from './${file.replace('.ts', ext)}'`) + } + + await writeFile(join(path, 'index.ts'), writer.toString()) + } + + /** + * Creates a package.json file in the output folder. + * Exports the files which are build by tsc based on generated client files + */ + async createPackageJson() { + const hasEsm = this.config.buildAs !== 'commonjs' + const hasCommonJs = this.config.buildAs !== 'esm' + + const packageJson: Record = { + name: 'my-client', + description: 'The client library package for a PURISTA based application', + private: true, + type: hasEsm ? 'module' : 'commonjs', + exports: { + './package.json': './package.json', + '.': {}, + }, + devDependencies: { + '@purista/core': 'latest', + }, + ...this.config.package, + } + + if (hasCommonJs) { + packageJson.main = './dist/commonjs/index.js' + packageJson.types = './dist/commonjs/index.d.ts' + packageJson.exports['.'] = { + ...packageJson.exports['.'], + require: { + types: './dist/commonjs/index.d.ts', + default: './dist/commonjs/index.js', + }, + } + } + + if (hasEsm) { + packageJson.exports['.'] = { + ...packageJson.exports['.'], + import: { + types: './dist/esm/index.d.ts', + default: './dist/esm/index.js', + }, + } + } + + await writeFile(join(this.getOutputPath(), 'package.json'), JSON.stringify(packageJson, null, 2)) + } + + /** + * Runs the tsc against the generated ts source files. + * Depending on settings, it will generate ESM and/or commonJS files + */ + build() { + const hasEsm = this.config.buildAs !== 'commonjs' + const hasCommonJs = this.config.buildAs !== 'esm' + + if (hasEsm) { + const esmOptions: ts.CompilerOptions = { + declaration: true, + outDir: join(this.getOutputPath(), 'dist', 'esm'), + target: ts.ScriptTarget.ES2022, + module: ts.ModuleKind.NodeNext, + skipLibCheck: true, + moduleResolution: ts.ModuleResolutionKind.NodeNext, + } + + this.compile([join(this.getOutputPath(), 'src', 'index.ts')], esmOptions, 'ESM') + } + + if (hasCommonJs) { + const commonJsOptions: ts.CompilerOptions = { + declaration: true, + outDir: join(this.getOutputPath(), 'dist', 'commonjs'), + skipLibCheck: true, + target: ts.ScriptTarget.ES2015, + module: ts.ModuleKind.CommonJS, + } + + this.compile([join(this.getOutputPath(), 'src', 'index.ts')], commonJsOptions, 'CommonJs') + } + } + + /** + * Internal helper for compiling typescript files + * @param fileNames + * @param options + * @param type + */ + private compile(fileNames: string[], options: ts.CompilerOptions, type: string) { + const program = ts.createProgram(fileNames, options) + + const emitResult = program.emit() + + const allDiagnostics = ts.getPreEmitDiagnostics(program).concat(emitResult.diagnostics) + + for (const diagnostic of allDiagnostics) { + if (diagnostic.file && diagnostic.start) { + const { line, character } = ts.getLineAndCharacterOfPosition(diagnostic.file, diagnostic.start) + const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n') + this.emit('warn', `${type} build: ${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`) + } else { + this.emit('warn', `${type} build: ${ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n')}`) + } + } + + const exitCode = emitResult.emitSkipped ? 1 : 0 + if (exitCode) { + this.emit('error', `${type} build: process exiting with code '${exitCode}'.`) + throw new Error(`${type} build: process exiting with code '${exitCode}'.`) + } + this.emit('success', `${type} build done`) + } + + /** + * Loads the definitions from JSON files + * @param path + * @returns + */ + async loadDefinitionFiles(path?: string): Promise { + this.emit('start', 'Start reading definitions') + + const services: FullServiceDefinition = {} + + const p = path ?? this.getDefinitionPath() + + const files = await readdir(p) + for (const file of files) { + if (!file.endsWith('.json')) { + continue + } + try { + const content = await readFile(join(p, file), { encoding: 'utf8' }) + + const json: FullDefinition = JSON.parse(content) + + if (json.services) { + mergeIntoServiceDefintion(services, json.services) + this.emit('success', file) + } + } catch (error) { + this.emit('error', error as Error) + } + } + + return services + } + + /** + * Generates the zero-dependency HTTP client source files + * @param serviceDefinition + */ + async generateHttpClient(serviceDefinition: FullServiceDefinition) { + const ext = this.config.buildAs !== 'commonjs' ? '.js' : '' + + const clientStream = createWriteStream(join(this.getOutputPath(), 'src', 'http_client.ts')) + const typeStream = createWriteStream(join(this.getOutputPath(), 'src', 'types_http_client.ts')) + + typeStream.write(this.getHttpClientConfigTypeString()) + + const clientWriter = getWriter() + + clientWriter + .writeLine(`import * as ClientType from './types_http_client${ext}'`) + .blankLine() + .writeLine(`export class ${this.config.httpClient.clientName} {`) + .blankLine() + .writeLine( + "public __config__: Omit & {baseUrl: string, traceIdHeaderName: string}", + ) + .blankLine() + .withIndentationLevel(1, () => { + clientWriter.write('constructor(config?: ClientType.HttpClientOptions)').block(() => { + clientWriter + .write('this.__config__ = ') + .block(() => { + clientWriter.writeLine(`baseUrl: 'http://localhost:3000/api',`) + clientWriter.writeLine('defaultTimeout: 30000,') + clientWriter.writeLine('isKeepAlive: true,') + clientWriter.writeLine("traceIdHeaderName: 'x-trace-id',") + clientWriter + .write('defaultHeaders: ') + .inlineBlock(() => { + clientWriter.writeLine("'Content-Type': 'application/json; utf-8',") + }) + .write(',') + clientWriter.writeLine('...config,') + }) + .blankLine() + }) + }) + + clientStream.write(clientWriter.toString()) + clientStream.write(` + /** + * Helper to create the url and HTTP headers + * @param path + * @param options + * @returns + */ + private __getUrlAndHeader__(path: string, options?: ClientType.HttpClientRequestOptions, traceId?: string) { + let fullPath = [...new URL(this.__config__.baseUrl).pathname?.split('/'),...path.split('/')].filter(p=>p).join('/') + if (options?.hash) { + fullPath += \`#\${options.hash}\` + } + + const url = new URL(fullPath, this.__config__.baseUrl) + + for (const [key, value] of Object.entries(options?.query ?? {})) { + url.searchParams.set(key, value) + } + + if (this.__config__.basicAuth) { + url.password = this.__config__.basicAuth.password + url.username = this.__config__.basicAuth.username + } + + const headers: Record = { + ...this.__config__.defaultHeaders, + ...options?.headers, + } + + if (this.__config__.bearerToken) { + headers['Authorization'] = \`Bearer \${this.__config__.bearerToken}\` + } + + if(traceId) { + headers[this.__config__.traceIdHeaderName ?? 'x-trace-id'] = traceId + } + + return { + url, + headers, + } + } + + /** + * Set the bearer token for requests + * */ + __setBearerToken__(bearerToken:string | undefined){ + this.__config__.bearerToken = bearerToken + } + + /** + * Helper method + * @param method + * @param path + * @param options + * @param payload + * @param traceId + * @throws Error + * @returns + */ + private async __execute__(method: string, path: string, options?: ClientType.HttpClientRequestOptions, payload?: unknown, trace?: string) { + const traceId = trace ?? crypto.randomUUID() + const { url, headers } = this.__getUrlAndHeader__(path, options, traceId) + + const controller = new AbortController() + const timeoutValue = options?.timeout ?? this.__config__.defaultTimeout + const timeout = setTimeout(() => { + controller.abort( + new HttpError(408, method, url.toString(), \`Request timeout exceeded \${timeoutValue} ms\`, undefined, traceId), + ) + }, timeoutValue) + + let body: string | undefined + + if (typeof payload === 'string') { + body = payload + } else { + body = payload ? JSON.stringify(payload) : undefined + } + + try { + const response = await fetch(url, { + method, + signal: controller.signal, + keepalive: this.__config__.isKeepAlive, + headers, + credentials: 'include', + body, + }) + + if (!response.ok) { + let body = '' + if (response.headers.get('content-type')?.startsWith('application/json')) { + body = await response.json() + } else { + body = await response.text() + } + + + const headers = Array.from(response.headers) + + throw new HttpError(response.status, method, url.toString(), \`\${response.statusText}\`, body, traceId) + } + + if (response.status === 204) { + return undefined + } + + if (response.headers.get('content-type')?.startsWith('application/json')) { + return await response.json() + } + return response.text() + } finally { + clearTimeout(timeout) + } + } + `) + + await this.generateHttpClientSource(clientStream, typeStream, serviceDefinition) + + clientStream.write('}') + + clientStream.write(this.getHttpErrorClassString()) + + await Promise.all([ + new Promise((resolve, _reject) => clientStream.end(() => resolve(undefined))), + new Promise((resolve, _reject) => typeStream.end(() => resolve(undefined))), + ]) + } + + /** + * Helper function which generates the getters + * @param clientStream + * @param typeStream + * @param serviceDefinitions + */ + private async generateHttpClientSource( + clientStream: WriteStream, + typeStream: WriteStream, + serviceDefinitions: FullServiceDefinition, + ) { + for (const [serviceName, serviceDefinition] of Object.entries(serviceDefinitions)) { + const writer = getWriter() + writer.newLine().withIndentationLevel(1, () => { + writer + .writeLine(`/** Service ${serviceName} */`) + .write(`get ${convertToCamelCase(serviceName)}()`) + .block(() => { + const s = Object.entries(serviceDefinition).reduce( + (input, [serviceVersion, def]) => { + const commands = Object.values(def.commands).filter(cmdDef => isHttpExposedServiceMeta(cmdDef.metadata)) + + if (!commands.length) { + return input + } + + return { + // biome-ignore lint/performance/noAccumulatingSpread: + ...input, + [serviceVersion]: commands.reduce((ret: string[], httpDef) => { + const meta = httpDef.metadata as unknown as HttpExposedServiceMeta + const { functionString, typeString } = metaToFunctionHttp( + serviceName, + serviceVersion, + httpDef.commandName, + meta, + ) + typeStream.write(typeString) + const final = ret + final.push(`/** ${httpDef.commandDescription} */`, `${httpDef.commandName}: ${functionString},`) + return final + }, [] as string[]), + } + }, + {} as { [key: string]: string[] }, + ) + + writer.write('return').block(() => { + for (const [serviceVersion, def] of Object.entries(s)) { + this.emit('info', `${serviceName} version ${serviceVersion}`) + writer.write(`'v${serviceVersion}':`).block(() => { + for (const line of def) { + writer.writeLine(line) + } + }) + } + }) + }) + .newLine() + }) + + clientStream.write(writer.toString()) + } + } + + /** + * Generates the correlating type file + * @returns + */ + private getHttpClientConfigTypeString() { + const writer = getWriter() + + writer.write('export type HttpClientOptions = ').block(() => { + writer.writeLine('/**') + writer.writeLine('* the base url to be used') + writer.writeLine('* @example') + writer.writeLine('* ```typescript') + writer.writeLine('* const config = {') + writer.writeLine("* baseUrl: 'http://localhost/api`") + writer.writeLine('* }') + writer.writeLine('* // each request will be below http://localhost/api') + writer.writeLine("* // get('v1/orders') will call http://localhost/api/v1/orders") + writer.writeLine('* ```') + writer.writeLine('* */') + writer.writeLine('baseUrl?: string') + + writer.writeLine('/**') + writer.writeLine('* If set to false, the HTTP client will not reuse the same connection for multiple requests.') + writer.writeLine('* @default true') + writer.writeLine('*/') + writer.writeLine('isKeepAlive?: boolean') + + writer.writeLine('/**') + writer.writeLine('* The header name for custom trace id') + writer.writeLine('* @default x-trace-id') + writer.writeLine('*/') + writer.writeLine('traceIdHeaderName?: string') + + writer.writeLine('/**') + writer.writeLine('* Add your default headers here') + writer.writeLine('* These headers will be part of every request.') + writer.writeLine('* They can be overwritten per request option') + writer.writeLine('* */') + writer.writeLine('defaultHeaders?: Record') + + writer.writeLine('/**') + writer.writeLine('* set global timeout for requests in ms') + writer.writeLine('* @default 30000') + writer.writeLine('*/') + writer.writeLine('defaultTimeout?: number') + + writer.writeLine('/**') + writer.writeLine('* Basic-Auth information') + writer.writeLine('*/') + writer.write('basicAuth?: ').block(() => { + writer.writeLine('/** Basic-Auth username */') + writer.writeLine('username: string') + writer.writeLine('/** Basic-Auth password */') + writer.writeLine('password: string') + }) + + writer.writeLine('/** Auth-Bearer token */') + writer.writeLine('bearerToken?: string') + }) + + writer.blankLine() + writer.write('export type HttpClientRequestOptions = ').block(() => { + writer + .writeLine('/**') + .writeLine('* additional headers') + .writeLine('*/') + .writeLine('headers?: Record') + .writeLine('/**') + .writeLine('* query/search string parameter') + .writeLine('*/') + .writeLine('query?: Record') + .writeLine('/**') + .writeLine('* url hash') + .writeLine('* @example: http://example.com/index.html#hash') + .writeLine('*/') + .writeLine('hash?: string') + .writeLine('/**') + .writeLine('* Timeout for the request in ms') + .writeLine('* @default 30000') + .writeLine('*/') + .writeLine('timeout?: number') + }) + + return writer.toString() + } + + /** + * Returns the source of HttpError class as string. + * @returns + */ + private getHttpErrorClassString() { + const writer = getWriter() + + writer.write('export class HttpError extends Error ').block(() => { + writer + .write( + 'constructor(public errorCode: number, public method: string, public url: string, message: string, public data?: unknown, public traceId?: string)', + ) + .block(() => { + writer + .writeLine('super(message)') + .writeLine('Error.captureStackTrace(this, this.constructor)') + .writeLine('Object.setPrototypeOf(this, HttpError.prototype)') + .writeLine('this.name = this.constructor.name') + }) + .blankLine() + .write('getErrorResponse(traceId?: string) ') + .block(() => { + writer + .write('const errorResponse = Object.freeze(') + .block(() => { + writer + .writeLine('status: this.errorCode,') + .writeLine('message: this.message,') + .writeLine('data: this.data,') + .writeLine('traceId: this.traceId ?? traceId,') + .writeLine('url: this.url,') + }) + .writeLine(')') + .writeLine('return errorResponse') + }) + .blankLine() + .write('toString()') + .block(() => { + writer.writeLine('return JSON.stringify(this.getErrorResponse())') + }) + .blankLine() + .write('toJSON()') + .block(() => { + writer.writeLine('return { stack: this.stack, name: this.name, ...this.getErrorResponse() }') + }) + }) + + return writer.toString() + } + + /** + * Generates the zero-dependency HTTP client source files + * @param serviceDefinition + */ + async generateHEventBridgeClient(serviceDefinition: FullServiceDefinition) { + const ext = this.config.buildAs !== 'commonjs' ? '.js' : '' + + const clientStream = createWriteStream(join(this.getOutputPath(), 'src', 'eventbridge_client.ts')) + const typeStream = createWriteStream(join(this.getOutputPath(), 'src', 'types_eventbridge_client.ts')) + + const clientWriter = getWriter() + + clientWriter + .writeLine(`import type { EventBridge } from '@purista/core'`) + .blankLine() + .writeLine(`import * as ClientType from './types_eventbridge_client${ext}'`) + .blankLine() + .writeLine(`export class ${this.config.eventBridgeClient.clientName} {`) + .blankLine() + .withIndentationLevel(1, () => { + clientWriter.write('constructor(public __eventBridge__: EventBridge)').block(() => {}) + }) + + clientStream.write(clientWriter.toString()) + + const typeWriter = getWriter() + + typeWriter.write('export type InvokeOptions = ').block(() => { + typeWriter.write('traceId?: string,') + typeWriter.write('principalId?: string,') + typeWriter.write('tenantId?: string,') + }) + + typeStream.write(typeWriter.toString()) + + await this.generateEventBridgeClientSource(clientStream, typeStream, serviceDefinition) + + clientStream.write('}') + + await Promise.all([ + new Promise((resolve, _reject) => clientStream.end(() => resolve(undefined))), + new Promise((resolve, _reject) => typeStream.end(() => resolve(undefined))), + ]) + } + + private generateEventBridgeClientSource( + clientStream: WriteStream, + typeStream: WriteStream, + serviceDefinitions: FullServiceDefinition, + ) { + for (const [serviceName, serviceDefinition] of Object.entries(serviceDefinitions)) { + const writer = getWriter() + writer.newLine().withIndentationLevel(1, () => { + writer + .writeLine(`/** Service ${serviceName} */`) + .write(`get ${convertToCamelCase(serviceName)}()`) + .block(() => { + const s = Object.entries(serviceDefinition).reduce( + (input, [serviceVersion, def]) => { + const commands = Object.values(def.commands) + + if (!commands.length) { + return input + } + + return { + // biome-ignore lint/performance/noAccumulatingSpread: + ...input, + [serviceVersion]: commands.reduce((ret: string[], httpDef) => { + const meta = httpDef.metadata as unknown as HttpExposedServiceMeta + const { functionString, typeString } = metaToFunctionBridge( + serviceName, + serviceVersion, + httpDef.commandName, + meta, + this.config.eventBridgeClient.clientName, + ) + typeStream.write(typeString) + const final = ret + final.push(`/** ${httpDef.commandDescription} */`, `${httpDef.commandName}: ${functionString},`) + return final + }, [] as string[]), + } + }, + {} as { [key: string]: string[] }, + ) + + writer.write('return').block(() => { + for (const [serviceVersion, def] of Object.entries(s)) { + this.emit('info', `${serviceName} version ${serviceVersion}`) + writer.write(`'v${serviceVersion}':`).block(() => { + for (const line of def) { + writer.writeLine(line) + } + }) + } + }) + }) + .newLine() + }) + + clientStream.write(writer.toString()) + } + } + + /** + * Destroys the builder and cleans the event listeners + */ + destroy() { + this.removeAllListeners() + } +} diff --git a/packages/core/src/ClientBuilder/getWriter.impl.ts b/packages/core/src/ClientBuilder/getWriter.impl.ts new file mode 100644 index 000000000..295e5e497 --- /dev/null +++ b/packages/core/src/ClientBuilder/getWriter.impl.ts @@ -0,0 +1,10 @@ +import CodeBlockWriter from 'code-block-writer' + +export const getWriter = () => + new CodeBlockWriter({ + // optional options + newLine: '\r\n', // default: "\n" + indentNumberOfSpaces: 2, // default: 4 + useTabs: false, // default: false + useSingleQuote: true, // default: false + }) diff --git a/packages/core/src/ClientBuilder/index.ts b/packages/core/src/ClientBuilder/index.ts new file mode 100644 index 000000000..0f2df7cd9 --- /dev/null +++ b/packages/core/src/ClientBuilder/index.ts @@ -0,0 +1,3 @@ +export * from './ClientBuilder.impl.js' +export * from './schema/index.js' +export * from './types/index.js' diff --git a/packages/core/src/ClientBuilder/mergeIntoServiceDefintion.impl.ts b/packages/core/src/ClientBuilder/mergeIntoServiceDefintion.impl.ts new file mode 100644 index 000000000..0a4f7e4e6 --- /dev/null +++ b/packages/core/src/ClientBuilder/mergeIntoServiceDefintion.impl.ts @@ -0,0 +1,18 @@ +import type { FullServiceDefinition } from '../helper/index.js' + +export const mergeIntoServiceDefintion = (current: FullServiceDefinition, add: FullServiceDefinition) => { + for (const [serviceName, value] of Object.entries(add)) { + if (current[serviceName]) { + for (const [serviceVersion, val] of Object.entries(value)) { + current[serviceName][serviceVersion] = { + description: current[serviceName][serviceVersion].description ?? val.description, + deprecated: current[serviceName][serviceVersion].deprecated, + commands: { ...val.commands, ...current[serviceName][serviceVersion].commands }, + subscriptions: { ...val.subscriptions, ...current[serviceName][serviceVersion].subscriptions }, + } + } + } else { + current[serviceName] = value + } + } +} diff --git a/packages/core/src/ClientBuilder/metaToFunctionBridge.impl.ts b/packages/core/src/ClientBuilder/metaToFunctionBridge.impl.ts new file mode 100644 index 000000000..3117c59bb --- /dev/null +++ b/packages/core/src/ClientBuilder/metaToFunctionBridge.impl.ts @@ -0,0 +1,75 @@ +import type { CommandDefinitionMetadataBase } from '../core/index.js' +import { schemaObjectToTsType } from '../helper/schemaObjectToTsType/transform.js' +import { convertToPascalCase } from '../helper/string/convertToPascalCase.impl.js' +import { getWriter } from './getWriter.impl.js' + +export const metaToFunctionBridge = ( + serviceName: string, + serviceVersion: string, + functionName: string, + meta: CommandDefinitionMetadataBase, + clientName: string, +): { + functionString: string + typeString: string +} => { + const codeWriter = getWriter() + const typeWriter = getWriter() + + const typeNamePrefix = `${convertToPascalCase(serviceName)}V${convertToPascalCase(serviceVersion)}${convertToPascalCase(functionName)}` + + const parameterType = meta.expose.parameter ? schemaObjectToTsType(meta.expose.parameter) : '{}' + const payloadType = meta.expose.inputPayload ? schemaObjectToTsType(meta.expose.inputPayload) : 'unknown' + const resposeType = meta.expose.outputPayload ? schemaObjectToTsType(meta.expose.outputPayload) : 'unknown' + + typeWriter + .blankLine() + .writeLine(`/** ${functionName} parameter type of ${serviceName} version ${serviceVersion} */`) + .writeLine(`export type ${typeNamePrefix}ParameterType = ${parameterType}`) + .blankLine() + .writeLine(`/** ${functionName} payload type of ${serviceName} version ${serviceVersion} */`) + .writeLine(`export type ${typeNamePrefix}PayloadType = ${payloadType}`) + .blankLine() + .writeLine(`/** ${functionName} return type of ${serviceName} version ${serviceVersion} */`) + .writeLine(`export type ${typeNamePrefix}ResposeType = ${resposeType}`) + + codeWriter + .writeLine( + `async (payload: ClientType.${typeNamePrefix}PayloadType, parameter: ClientType.${typeNamePrefix}ParameterType, options?: ClientType.InvokeOptions) => `, + ) + .block(() => { + codeWriter + .write(`return this.__eventBridge__.invoke(`) + .block(() => { + codeWriter + .write('sender:') + .inlineBlock(() => { + codeWriter + .writeLine(`serviceName: '${clientName}',`) + .writeLine(`serviceVersion: '1',`) + .writeLine(`serviceTarget: '__client_invoke__',`) + .writeLine('instanceId: this.__eventBridge__.instanceId,') + }) + .write(', receiver:') + .inlineBlock(() => { + codeWriter + .writeLine(`serviceName: '${serviceName}',`) + .writeLine(`serviceVersion: '${serviceVersion}',`) + .writeLine(`serviceTarget: '${functionName}',`) + }) + .write(', payload: ') + .block(() => { + codeWriter.writeLine('payload,').writeLine('parameter,') + }) + .writeLine(`, contentType: '${meta.expose.contentTypeRequest}',`) + .writeLine(`contentEncoding: '${meta.expose.contentEncodingRequest}',`) + .writeLine('...options,') + }) + .write(')') + }) + + return { + functionString: codeWriter.toString(), + typeString: typeWriter.toString(), + } +} diff --git a/packages/core/src/ClientBuilder/metaToFunctionHttp.impl.ts b/packages/core/src/ClientBuilder/metaToFunctionHttp.impl.ts new file mode 100644 index 000000000..5e92b3e83 --- /dev/null +++ b/packages/core/src/ClientBuilder/metaToFunctionHttp.impl.ts @@ -0,0 +1,140 @@ +import type { HttpExposedServiceMeta } from '../core/index.js' +import { schemaObjectToTsType } from '../helper/schemaObjectToTsType/transform.js' +import { convertToPascalCase } from '../helper/string/convertToPascalCase.impl.js' +import { getWriter } from './getWriter.impl.js' + +export const metaToFunctionHttp = ( + serviceName: string, + serviceVersion: string, + functionName: string, + meta: HttpExposedServiceMeta, +): { + functionString: string + typeString: string +} => { + const codeWriter = getWriter() + const typeWriter = getWriter() + + const typeNamePrefix = `${convertToPascalCase(serviceName)}V${convertToPascalCase(serviceVersion)}${convertToPascalCase(functionName)}` + + const pathParams: { name: string; required: boolean }[] = [] + const path = `\`v${serviceVersion}/${meta.expose.http.path + .split('/') + .map(part => { + if (!part.startsWith(':')) { + return part + } + const name = part.slice(1).replace('?', '') + pathParams.push({ name, required: !part.endsWith('?') }) + return `\${parameter.${name} ?? '' }` + }) + .join('/')}\`` + + const queries = meta.expose.http.openApi?.query + + const params: { + name: string + required: boolean + }[] = [...pathParams] + + if (queries) { + for (const query of queries) { + params.push(query) + } + } + + let fnParamString = '' + + if (params.length) { + const hasRequired = params.some(entry => entry.required) + + fnParamString = `parameter${hasRequired ? '' : '?'}: ClientType.${typeNamePrefix}ParameterType` + + typeWriter + .blankLine() + .write(`export type ${typeNamePrefix}ParameterType = `) + .block(() => { + for (const param of params) { + typeWriter.writeLine(`${param.name}${param.required ? '' : '?'}: string,`) + } + }) + } + + let returnType = 'void' + if (meta.expose.outputPayload) { + const typeFromSchema = schemaObjectToTsType(meta.expose.outputPayload) + + const genType = typeFromSchema.trim().toLowerCase() + + if (genType !== 'undefined' && genType !== 'void') { + returnType = `${typeNamePrefix}ReturnType` + typeWriter + .blankLine() + .writeLine(`/** ${functionName} return type of ${serviceName} version ${serviceVersion} */`) + .writeLine(`export type ${returnType} = ${typeFromSchema}`) + } + } + + let hasPayload = ['post', 'patch', 'put', 'delete'].includes(meta.expose.http.method.toLocaleLowerCase()) + + let payloadType = 'unknown' + const payloadTypeName = `${typeNamePrefix}PayloadType` + if (hasPayload) { + if (meta.expose.inputPayload) { + payloadType = schemaObjectToTsType(meta.expose.inputPayload) + + const genType = payloadType.trim().toLowerCase() + if (genType === 'undefined' || genType === 'void') { + hasPayload = false + } + } + + typeWriter + .blankLine() + .writeLine(`/** ${functionName} payload type of ${serviceName} version ${serviceVersion} */`) + .writeLine(`export type ${payloadTypeName} = ${payloadType}`) + } + + const writeOptions = () => { + for (const query of queries ?? []) { + codeWriter.writeLine( + `if(parameter${query.required ? '' : '?'}.${query.name}) { options.query['${query.name}'] = parameter.${query.name}}`, + ) + } + + codeWriter + .blankLine() + .write('options.headers = ') + .block(() => { + codeWriter.writeLine( + `'Content-Type': '${meta.expose.contentTypeRequest ?? 'application/json'}; ${meta.expose.contentEncodingRequest ?? 'utf-8'}',`, + ) + }) + } + + if (hasPayload) { + const addParameters = fnParamString === '' ? '' : `, ${fnParamString}` + codeWriter + .write(`async (payload: ClientType.${payloadTypeName}${addParameters}): Promise =>`) + .block(() => { + codeWriter.writeLine('const options = { query: {}, headers: {} } satisfies ClientType.HttpClientRequestOptions') + writeOptions() + codeWriter.write( + `return this.__execute__('${meta.expose.http.method.toLocaleLowerCase()}', ${path}, options, payload)`, + ) + }) + } else { + codeWriter.write(`async (${fnParamString}): Promise =>`).block(() => { + codeWriter.writeLine('const options = { query: {}, headers: {} } satisfies ClientType.HttpClientRequestOptions') + writeOptions() + codeWriter.write( + `return this.__execute__('${meta.expose.http.method.toLocaleLowerCase()}', ${path}, options, undefined)`, + ) + }) + } + + return { + functionString: codeWriter.toString(), + typeString: typeWriter.toString(), + } +} diff --git a/packages/core/src/ClientBuilder/schema/configSchema.ts b/packages/core/src/ClientBuilder/schema/configSchema.ts new file mode 100644 index 000000000..40a1fd99f --- /dev/null +++ b/packages/core/src/ClientBuilder/schema/configSchema.ts @@ -0,0 +1,32 @@ +import { z } from 'zod' + +import { eventBridgeClientConfigSchema } from './eventBridgeClientConfigSchema.js' +import { httpClientConfigSchema } from './httpClientConfigSchema.js' + +const clientGenerateBaseConfigSchema = z.object({ + version: z.string(), + definitionPath: z.string(), + outputPath: z.string(), + buildAs: z.enum(['esm', 'commonjs', 'both']).default('both'), + package: z + .object({ + name: z.string(), + description: z.string().default('The client library package for a PURISTA based application'), + private: z.boolean().default(true), + }) + .optional(), +}) + +export const configSchema = clientGenerateBaseConfigSchema.merge( + z.object({ + httpClient: httpClientConfigSchema.optional(), + eventBridgeClient: eventBridgeClientConfigSchema.optional(), + }), +) + +export const configFullSchema = clientGenerateBaseConfigSchema.merge( + z.object({ + httpClient: httpClientConfigSchema, + eventBridgeClient: eventBridgeClientConfigSchema, + }), +) diff --git a/packages/core/src/ClientBuilder/schema/eventBridgeClientConfigSchema.ts b/packages/core/src/ClientBuilder/schema/eventBridgeClientConfigSchema.ts new file mode 100644 index 000000000..da467476f --- /dev/null +++ b/packages/core/src/ClientBuilder/schema/eventBridgeClientConfigSchema.ts @@ -0,0 +1,5 @@ +import { z } from 'zod' + +export const eventBridgeClientConfigSchema = z.object({ + clientName: z.string().default('EventBridgeClient'), +}) diff --git a/packages/core/src/ClientBuilder/schema/httpClientConfigSchema.ts b/packages/core/src/ClientBuilder/schema/httpClientConfigSchema.ts new file mode 100644 index 000000000..54e9eed08 --- /dev/null +++ b/packages/core/src/ClientBuilder/schema/httpClientConfigSchema.ts @@ -0,0 +1,5 @@ +import { z } from 'zod' + +export const httpClientConfigSchema = z.object({ + clientName: z.string().default('HttpClient'), +}) diff --git a/packages/core/src/ClientBuilder/schema/index.ts b/packages/core/src/ClientBuilder/schema/index.ts new file mode 100644 index 000000000..8140f7732 --- /dev/null +++ b/packages/core/src/ClientBuilder/schema/index.ts @@ -0,0 +1,3 @@ +export * from './configSchema.js' +export * from './eventBridgeClientConfigSchema.js' +export * from './httpClientConfigSchema.js' diff --git a/packages/core/src/ClientBuilder/types/ClientBuilderConfig.ts b/packages/core/src/ClientBuilder/types/ClientBuilderConfig.ts new file mode 100644 index 000000000..698f3c06a --- /dev/null +++ b/packages/core/src/ClientBuilder/types/ClientBuilderConfig.ts @@ -0,0 +1,5 @@ +import type { z } from 'zod' + +import type { httpClientConfigSchema } from '../schema/httpClientConfigSchema.js' + +export type ClientBuilderConfig = z.infer diff --git a/packages/core/src/ClientBuilder/types/ClientBuilderEvents.ts b/packages/core/src/ClientBuilder/types/ClientBuilderEvents.ts new file mode 100644 index 000000000..74997c2a0 --- /dev/null +++ b/packages/core/src/ClientBuilder/types/ClientBuilderEvents.ts @@ -0,0 +1,7 @@ +export type ClientBuilderEvents = { + info: string + warn: string + error: string | Error + success: string + start: string +} diff --git a/packages/core/src/ClientBuilder/types/Config.ts b/packages/core/src/ClientBuilder/types/Config.ts new file mode 100644 index 000000000..b200b34ed --- /dev/null +++ b/packages/core/src/ClientBuilder/types/Config.ts @@ -0,0 +1,7 @@ +import type { z } from 'zod' + +import type { configFullSchema, configSchema } from '../schema/configSchema.js' + +export type Config = z.infer + +export type ConfigFull = z.infer diff --git a/packages/core/src/ClientBuilder/types/index.ts b/packages/core/src/ClientBuilder/types/index.ts new file mode 100644 index 000000000..f00ac02a4 --- /dev/null +++ b/packages/core/src/ClientBuilder/types/index.ts @@ -0,0 +1,3 @@ +export * from './ClientBuilderConfig.js' +export * from './ClientBuilderEvents.js' +export * from './Config.js' diff --git a/packages/core/src/CommandDefinitionBuilder/CommandDefinitionBuilder.impl.ts b/packages/core/src/CommandDefinitionBuilder/CommandDefinitionBuilder.impl.ts index addf849b1..8016b6574 100644 --- a/packages/core/src/CommandDefinitionBuilder/CommandDefinitionBuilder.impl.ts +++ b/packages/core/src/CommandDefinitionBuilder/CommandDefinitionBuilder.impl.ts @@ -1,29 +1,35 @@ import type { Infer, InferIn, Schema } from '@typeschema/main' import type { SinonSandbox } from 'sinon' -import type { ZodAny } from 'zod' -import { - type CommandAfterGuardHook, - type CommandBeforeGuardHook, - type CommandDefinition, - type CommandDefinitionMetadataBase, - type CommandFunction, - type CommandTransformInputHook, - type CommandTransformOutputHook, - type Complete, - type ContentType, - type DefinitionEventBridgeConfig, - type FromInvokeToOtherType, - type HttpExposedServiceMeta, - type QueryParameter, - type ServiceClass, - StatusCode, - type SupportedHttpMethod, - UnhandledError, +import type { + CommandAfterGuardHook, + CommandBeforeGuardHook, + CommandDefinition, + CommandDefinitionMetadataBase, + CommandFunction, + CommandTransformInputHook, + CommandTransformOutputHook, + Complete, + ContentType, + DefinitionEventBridgeConfig, + GetMessageParamsType, + GetMessagePayloadType, + HttpExposedServiceMeta, + InferTypeOrEmptyObject, + InvokeList, + QueryParameter, + Service, + SupportedHttpMethod, } from '../core/index.js' -import { type NonEmptyString } from '../helper/index.js' +import { StatusCode, UnhandledError } from '../core/index.js' +import { + type NonEmptyString, + convertEmitValidationsToSchema, + convertInvokeValidationsToSchema, +} from '../helper/index.js' import { getCommandContextMock, getCommandTransformContextMock } from '../mocks/index.js' import { validationToSchema } from '../zodOpenApi/index.js' +import type { CommandDefinitionBuilderTypes } from './CommandDefinitionBuilderTypes.js' import { getCommandFunctionWithValidation } from './getCommandFunctionWithValidation.impl.js' /** @@ -36,925 +42,987 @@ import { getCommandFunctionWithValidation } from './getCommandFunctionWithValida * @group Command */ export class CommandDefinitionBuilder< - ServiceClassType extends ServiceClass, - MessagePayloadType = unknown, - MessageParamsType = {}, - MessageResultType = void, - PayloadSchema extends Schema = ZodAny, - ParameterSchema extends Schema = ZodAny, - ResultSchema extends Schema = ZodAny, - Invokes = {}, - EmitListType = {}, + S extends Service, + C extends CommandDefinitionBuilderTypes = CommandDefinitionBuilderTypes, > { - private httpMetadata?: HttpExposedServiceMeta> - private inputSchema?: PayloadSchema - private inputContentType: ContentType | undefined - private inputContentEncoding: string | undefined - private outputSchema?: ResultSchema - private outputContentType: ContentType | undefined - private outputContentEncoding: string | undefined - private parameterSchema?: ParameterSchema - private queryParameter: QueryParameter>[] = [] - - private tags: string[] = [] - - private deprecated = false - - private summary?: string - - private errorStatusCodes: StatusCode[] = [] - - private isSecure = true - - private operationId?: string - - private durable = false - private autoacknowledge = true - - private invokes: FromInvokeToOtherType< - Invokes, - { outputSchema?: Schema; payloadSchema?: Schema; parameterSchema?: Schema } - > = {} as FromInvokeToOtherType - - private emitList: EmitListType = {} as EmitListType - - private hooks: { - transformInput?: { - transformInputSchema: Schema - transformParameterSchema: Schema - transformFunction: CommandTransformInputHook - } - beforeGuard: Record< - string, - CommandBeforeGuardHook< - ServiceClassType, - MessagePayloadType, - MessageParamsType, - Infer, - Infer, - Invokes, - EmitListType - > - > - afterGuard: Record< - string, - CommandAfterGuardHook< - ServiceClassType, - MessagePayloadType, - MessageParamsType, - Infer, - Infer, - Infer, - Invokes, - EmitListType - > - > - transformOutput?: { - transformOutputSchema: Schema - transformFunction: CommandTransformOutputHook - } - } = { - transformInput: undefined, - beforeGuard: {}, - afterGuard: {}, - transformOutput: undefined, - } - - private fn?: CommandFunction< - ServiceClassType, - MessagePayloadType, - MessageParamsType, - Infer, - Infer, - InferIn, - Invokes, - EmitListType - > - - // eslint-disable-next-line no-useless-constructor - constructor( - private commandName: Exclude, - private commandDescription: string, - private eventName?: Exclude, - ) {} - - /** - * Define a command which can be invoked by the current command - * @param serviceName - * @param serviceVersion - * @param serviceTarget - * @param outputSchema - * @param payloadSchema - * @param parameterSchema - * @returns - */ - canInvoke< - Output extends Schema, - Payload extends Schema, - Parameter extends Schema, - SName extends string = string, - Version extends string = string, - Fname extends string = string, - >( - serviceName: SName, - serviceVersion: Version, - serviceTarget: Fname, - outputSchema?: Output, - payloadSchema?: Payload, - parameterSchema?: Parameter, - ) { - if (serviceName.trim() === '' || serviceVersion.trim() === '' || serviceTarget.trim() === '') { - throw new Error('canInvoke requires non-empty service name, version and target') - } - - const x = this.invokes as any - if (!x[serviceName]) { - x[serviceName] = {} - } - - if (!x[serviceName][serviceVersion]) { - x[serviceName][serviceVersion] = {} - } - - const f = { - [serviceName]: { - [serviceVersion]: { - [serviceTarget]: { outputSchema, payloadSchema, parameterSchema }, - }, - }, - } as Invokes & - Record< - SName, - Record< - Version, - Record, parameter: InferIn) => Promise>> - > - > - - this.invokes = { - ...this.invokes, - ...f, - } - - return this as CommandDefinitionBuilder< - ServiceClassType, - MessagePayloadType, - MessageParamsType, - MessageResultType, - PayloadSchema, - ParameterSchema, - ResultSchema, - Invokes & - Record< - SName, - Record< - Version, - Record, parameter: InferIn) => Promise>> - > - >, - EmitListType - > - } - - /** - * Define which custom events the command can emit. - * - * @param eventName The custom event name - * @param schema the payload schema - * @returns - */ - canEmit(eventName: EventName, schema: T) { - if (eventName.trim() === '') { - throw new Error('canEmit requires non-empty event name') - } - - this.emitList = { ...this.emitList, [eventName]: schema } - - return this as CommandDefinitionBuilder< - ServiceClassType, - MessagePayloadType, - MessageParamsType, - MessageResultType, - PayloadSchema, - ParameterSchema, - ResultSchema, - Invokes, - EmitListType & Record> - > - } - - /* - * Event name of success response message. - * This makes it easy to subscribe to something like `UserCreated` (optional add service version to the subscription). - * Otherwise you will need to subscribe to service name, service function, and message type to get the message. - * It is also essential to have this possibility to be able to build event sourcing architectures. - */ - setSuccessEventName(eventName: NonEmptyString) { - this.eventName = eventName - return this - } - - /** - * Add a schema for input payload validation. - * Types for payload of message and function payload input are generated from given schema. - * @param inputSchema The schema validation for input payload - * @param inputContentType optional the content type of payload - * @param inputContentEncoding optional the content encoding - * @returns CommandDefinitionBuilder - */ - addPayloadSchema(inputSchema: T, inputContentType?: ContentType, inputContentEncoding?: string) { - this.inputContentType = inputContentType ?? this.inputContentType - this.inputContentEncoding = inputContentEncoding ?? this.inputContentEncoding - this.inputSchema = inputSchema as unknown as PayloadSchema - return this as unknown as CommandDefinitionBuilder< - ServiceClassType, - InferIn, - MessageParamsType, - MessageResultType, - T, - ParameterSchema, - ResultSchema, - Invokes, - EmitListType - > - } - - /** - * Add a schema for output payload validation. - * Types for payload of message and function payload output are generated from given schema. - * @param outputSchema The schema validation for output payload - * @param outputContentType optional the content type of payload - * @param outputContentEncoding optional the content encoding - * @returns CommandDefinitionBuilder - */ - addOutputSchema(outputSchema: T, outputContentType?: ContentType, outputContentEncoding?: string) { - this.outputContentType = outputContentType ?? this.outputContentType - this.outputContentEncoding = outputContentEncoding ?? this.outputContentEncoding - this.outputSchema = outputSchema as unknown as ResultSchema - return this as unknown as CommandDefinitionBuilder< - ServiceClassType, - MessagePayloadType, - MessageParamsType, - Infer, - PayloadSchema, - ParameterSchema, - T, - Invokes, - EmitListType - > - } - - /** - * Mark this endpoint/command as deprecated - * @returns CommandDefinitionBuilder - */ - markAsDeprecated() { - this.deprecated = true - return this - } - - /** - * Add a schema for output parameter validation. - * Types for parameter of message and function parameter output are generated from given schema. - * @param parameterSchema The schema validation for output parameter - * @returns CommandDefinitionBuilder - */ - addParameterSchema(parameterSchema: T) { - this.parameterSchema = parameterSchema as unknown as ParameterSchema - return this as unknown as CommandDefinitionBuilder< - ServiceClassType, - MessagePayloadType, - InferIn, - MessageResultType, - PayloadSchema, - T, - ResultSchema, - Invokes, - EmitListType - > - } - - /** - * Define query parameters if you expose the function as http endpoint. - * Query parameters are add to openApi definition. - * Query parameters are add to input parameters. - * - * @example - * ```ts - * .addQueryParameters( - * { - * required: false, - * name: 'search', - * }, - * { - * required: false, - * name: 'limit', - * }, - * ) - * ``` - * - * @param queryParams Add one or more query parameter definitions - * @returns CommandDefinitionBuilder - */ - addQueryParameters(...queryParams: QueryParameter>[]) { - this.queryParameter.push(...queryParams) - return this - } - - /** - * Add tags for openApi definition for given function. - * It is recommended to use some enum for tags to avoid typo issues. - * - * @example - * ```ts - * addTags('User','Public') - * ``` - * - * @param tags List of tag strings - * @returns CommandDefinitionBuilder - */ - addOpenApiTags(...tags: string[]) { - this.tags.push(...tags) - return this - } - - /** - * If a function can return other status codes, than the default ones, you should add them to openApi definition. - * Per default, 200, 204, 400, 401 and 500 can be autogenerated in most cases. - * Special cases or different status codes should be added with this function. - * - * @example - * ```ts - * addErrorStatusCodes(StatusCode.PaymentRequired, StatusCode.Conflict) - * ``` - * - * @param codes List of status codes - * @returns CommandDefinitionBuilder - */ - addOpenApiErrorStatusCodes(...codes: StatusCode[]) { - this.errorStatusCodes.push(...codes) - return this - } - - /** - * Set a transform input hook which will encode or transform the input payload and parameters. - * Will be executed as first step before input validation, before guard and the function itself. - * This will change the type of input message payload and input message parameter. - * @param transformInputSchema Input payload validation schema - * @param transformParameterSchema Input parameter validation schema - * @param transformFunction Transform input function - * @param inputContentType optional the content type of payload - * @param inputContentEncoding optional the content encoding - * @returns CommandDefinitionBuilder - */ - setTransformInput( - transformInputSchema: TransFormPayloadSchema, - transformParameterSchema: TransFormParameterSchema, - transformFunction: CommandTransformInputHook< - ServiceClassType, - InferIn, - InferIn, - Infer, - Infer - >, - inputContentType?: ContentType, - inputContentEncoding?: string, - ) { - this.inputContentType = inputContentType ?? this.inputContentType - this.inputContentEncoding = inputContentEncoding ?? this.inputContentEncoding - - this.hooks.transformInput = { - transformFunction, - transformInputSchema, - transformParameterSchema, - } - return this as unknown as CommandDefinitionBuilder< - ServiceClassType, - InferIn, - InferIn, - MessageResultType, - PayloadSchema, - ParameterSchema, - ResultSchema, - Invokes, - EmitListType - > - } - - /** - * Return the transform input function - * @returns the input transform function if defined - */ - getTransformInputFunction() { - if (!this.hooks.transformInput) { - return undefined - } - - return this.hooks.transformInput.transformFunction as CommandTransformInputHook< - ServiceClassType, - InferIn, - InferIn, - MessagePayloadType, - MessageParamsType - > - } - - /** - * Set a transform output hook which will encode or transform the response payload. - * Will be executed at very last step after function execution, output validation and after guard hooks. - * This will change the type of output message payload. - * @param transformOutputSchema The output validation schema - * @param transformFunction Transform output function - * @param outputContentType optional the content type of payload - * @param outputContentEncoding optional the content encoding - * @returns CommandDefinitionBuilder - */ - setTransformOutput( - transformOutputSchema: Output, - transformFunction: CommandTransformOutputHook< - ServiceClassType, - MessagePayloadType, - MessageParamsType, - InferIn, - Infer, - Infer - >, - - outputContentType?: ContentType, - outputContentEncoding?: string, - ) { - this.outputContentEncoding = outputContentEncoding ?? this.outputContentEncoding - this.outputContentType = outputContentType ?? this.outputContentType - this.hooks.transformOutput = { - transformFunction, - transformOutputSchema, - } - return this as unknown as CommandDefinitionBuilder< - ServiceClassType, - MessagePayloadType, - MessageParamsType, - Infer, - PayloadSchema, - ParameterSchema, - ResultSchema, - Invokes, - EmitListType - > - } - - /** - * Return the transform output function - * @returns the transform output function if defined - */ - getTransformOutputFunction() { - if (!this.hooks.transformOutput) { - return undefined - } - - return this.hooks.transformOutput.transformFunction as CommandTransformOutputHook< - ServiceClassType, - MessagePayloadType, - MessageParamsType, - MessageResultType, - Infer, - Infer - > - } - - /** - * Set one or more before guard hook(s). - * If there are multiple before guard hooks, they are executed in parallel - * @param beforeGuards Object of key = name of guard, value = function - * @returns CommandDefinitionBuilder - */ - setBeforeGuardHooks( - beforeGuards: Record< - string, - CommandBeforeGuardHook< - ServiceClassType, - MessagePayloadType, - MessageParamsType, - Infer, - Infer, - Invokes, - EmitListType - > - >, - ) { - this.hooks.beforeGuard = { ...this.hooks.beforeGuard, ...beforeGuards } - return this - } - - /** - * Set one or more after guard hook(s). - * If there are multiple after guard hooks, they are executed in parallel - * @param afterGuard Object of key = name of guard, value = function - * @returns CommandDefinitionBuilder - */ - setAfterGuardHooks( - afterGuards: Record< - string, - CommandAfterGuardHook< - ServiceClassType, - MessagePayloadType, - MessageParamsType, - Infer, - Infer, - Infer, - Invokes, - EmitListType - > - >, - ) { - this.hooks.afterGuard = { ...this.hooks.afterGuard, ...afterGuards } - return this - } - - /** - * Mark the function to be exposed as http endpoint. - * - * Api url prefix and service version are prepended automatically - * - * For exposing a url like: `/api/V1/user/login` simply provide `user/login`as path - * - * @param method Http method POST, PUT, PATCH, GET, DELETE - * @param path The url path - * @param contentTypeRequest input content type defaults to application/json - * @param contentEncodingRequest input content encoding defaults to utf-8 - * @param contentTypeResponse input content type defaults to application/json - * @param contentEncodingResponse input content encoding defaults to utf-8 - * @returns CommandDefinitionBuilder - */ - exposeAsHttpEndpoint( - method: SupportedHttpMethod, - path: string, - contentTypeRequest?: ContentType, - contentEncodingRequest?: string, - contentTypeResponse?: ContentType, - contentEncodingResponse?: string, - ) { - this.httpMetadata = { - expose: { - contentTypeRequest: contentTypeRequest ?? this.inputContentType ?? 'application/json', - contentEncodingRequest: contentEncodingRequest ?? this.inputContentEncoding ?? 'utf-8', - contentTypeResponse: contentTypeResponse ?? this.outputContentType ?? 'application/json', - contentEncodingResponse: contentEncodingResponse ?? this.outputContentEncoding ?? 'utf-8', - http: { - method, - path, - }, - }, - } - return this - } - - /** - * enable or disable security for this endpoint - * @param enabled Defaults to true if not set means "enable security" - * @returns CommandDefinitionBuilder - */ - enableHttpSecurity(enabled = true) { - this.isSecure = enabled - return this - } - - /** - * enable or disable security for this endpoint - * @param enabled Defaults to tre if not set meaning "disable security" - * @returns CommandDefinitionBuilder - */ - disableHttpSecurity(disabled = true) { - this.isSecure = !disabled - return this - } - - /** - * Set the function summary text used for example in openApi documentation - * - * @example - * ```ts - * setSummary('Some function summary') - * ``` - * - * @param summary Summary text - * @returns CommandDefinitionBuilder - */ - setOpenApiSummary(summary: string) { - this.summary = summary - return this - } - - /** - * Set the operationId for openApi documentation - * @param operationId - * @returns CommandDefinitionBuilder - */ - setOpenApiOperationId(operationId: string) { - this.operationId = operationId - return this - } - - private extendWithHttpMetadata( - definition: Complete< - CommandDefinition< - ServiceClassType, - CommandDefinitionMetadataBase, - MessagePayloadType, - MessageParamsType, - MessageResultType, - Infer, - Infer, - Infer, - Invokes, - EmitListType - > - >, - ) { - if (!this.httpMetadata) { - return definition - } - - const def: Complete< - CommandDefinition< - ServiceClassType, - HttpExposedServiceMeta>, - MessagePayloadType, - MessageParamsType, - MessageResultType, - Infer, - Infer, - Infer, - Invokes, - EmitListType - > - > = { - ...definition, - metadata: { - ...definition.metadata, - expose: { - ...definition.metadata?.expose, - ...this.httpMetadata.expose, - }, - }, - } - - def.metadata.expose.http.openApi = { - description: this.commandDescription, - summary: this.summary ?? this.commandName, - isSecure: this.isSecure, - query: this.queryParameter, - tags: this.tags, - additionalStatusCodes: this.errorStatusCodes, - operationId: this.operationId ?? this.commandName, - } - - return def - } - - /** - * Instruct the event bridge message broker to autoacknowledge commands as soon as they arrive. - * This means, a message will not be resent, if the command execution fails unexpected. - * - * If set to false, the command message will be resent from message broker to eventbridge, if: - * - the underlaying message broker supports it - * - if the command execution fails unexpected - * - if sending of command response failed - * - * @param acknowledge Enable (true) and disable (false) - * @returns CommandDefinition - */ - adviceAutoacknowledgeMessages(acknowledge = true) { - this.autoacknowledge = acknowledge - return this - } - - /** - * Creates and returns the CommandDefinition used as input for the service. - * @returns CommandDefinition - */ - async getDefinition(): Promise< - CommandDefinition< - ServiceClassType, - CommandDefinitionMetadataBase, - MessagePayloadType, - MessageParamsType, - MessageResultType, - Infer, - Infer, - Infer, - Invokes, - EmitListType - > - > { - if (!this.fn) { - throw new Error('CommandDefinitionBuilder: missing function implementation') - } - - const eventName = this.eventName - - const inputPayloadSchema: Schema | undefined = this.hooks.transformInput?.transformInputSchema ?? this.inputSchema - const inputParameterSchema: Schema | undefined = - this.hooks.transformInput?.transformParameterSchema ?? this.parameterSchema - const outputPayloadSchema: Schema | undefined = - this.hooks.transformOutput?.transformOutputSchema ?? this.outputSchema - - const eventBridgeConfig: Complete = { - durable: this.durable, - autoacknowledge: this.autoacknowledge, - shared: true, - } - - const [inputPayload, parameter, outputPayload] = await Promise.all([ - validationToSchema(inputPayloadSchema), - validationToSchema(inputParameterSchema), - validationToSchema(outputPayloadSchema), - ]) - - const definition: Complete< - CommandDefinition< - ServiceClassType, - CommandDefinitionMetadataBase, - MessagePayloadType, - MessageParamsType, - MessageResultType, - Infer, - Infer, - Infer, - Invokes, - EmitListType - > - > = { - commandName: this.commandName, - commandDescription: this.commandDescription, - eventBridgeConfig, - metadata: { - expose: { - contentTypeRequest: this.inputContentType ?? 'application/json', - contentEncodingRequest: this.inputContentEncoding ?? 'utf-8', - contentTypeResponse: this.outputContentType ?? 'application/json', - contentEncodingResponse: this.outputContentEncoding ?? 'utf-8', - inputPayload, - parameter, - outputPayload, - deprecated: this.deprecated, - }, - }, - eventName, - call: this.getCommandFunction(), - hooks: this.hooks, - invokes: this.invokes, - emitList: this.emitList, - } - - return this.extendWithHttpMetadata(definition) - } - - /** - * Required: Set the function implementation. - * The types should be automatically set as soon as schemas previously defined. - * As the function will be a a function of a service class you need to implement as function declaration. - * Anonymous functions do not have access to the `this` scope. - * - * @example - * ```ts - * async function (context, payload, parameter) { - * - * return `the result output payload` - * } - * ``` - * @param fn the function implementation - * @returns CommandDefinitionBuilder - */ - public setCommandFunction( - fn: CommandFunction< - ServiceClassType, - MessagePayloadType, - MessageParamsType, - Infer, - Infer, - InferIn, - Invokes, - EmitListType - >, - ): CommandDefinitionBuilder< - ServiceClassType, - MessagePayloadType, - MessageParamsType, - MessageResultType, - PayloadSchema, - ParameterSchema, - ResultSchema, - Invokes, - EmitListType - > { - this.fn = fn as unknown as CommandFunction< - ServiceClassType, - MessagePayloadType, - MessageParamsType, - Infer, - Infer, - InferIn, - Invokes, - EmitListType - > - - return this as unknown as CommandDefinitionBuilder< - ServiceClassType, - MessagePayloadType, - MessageParamsType, - MessageResultType, - PayloadSchema, - ParameterSchema, - ResultSchema, - Invokes, - EmitListType - > - } - - /** - * Get the function implementation including input and output validation. - * Also, before and after hooks are triggered during execution. - * - * @returns the function - */ - getCommandFunction() { - if (!this.fn) { - throw new UnhandledError(StatusCode.NotImplemented, `No function implementation for ${this.commandName}`, { - commandName: this.commandName, - }) - } - - const f: CommandFunction< - ServiceClassType, - MessagePayloadType, - MessageParamsType, - InferIn, - InferIn, - Infer, - Invokes, - EmitListType - > = getCommandFunctionWithValidation( - this.fn, - this.inputSchema, - this.parameterSchema, - this.outputSchema, - this.hooks.beforeGuard, - ) - - return f - } - - /** - * Get the function implementation without input and output validation. - * No hooks are triggered during execution. - * - * @returns the function - */ - getCommandFunctionPlain(): CommandFunction< - ServiceClassType, - MessagePayloadType, - MessageParamsType, - Infer, - Infer, - InferIn, - Invokes, - EmitListType - > { - if (!this.fn) { - throw new UnhandledError(StatusCode.NotImplemented, `No function implementation for ${this.commandName}`, { - commandName: this.commandName, - }) - } - - return this.fn - } - - /** - * Returns a mocked command function context, which can be used in unit tests. - * - * @param payload - * @param parameter - * @param sandbox Sinon sandbox - * @returns a mocked command function context - */ - getCommandContextMock(payload: MessagePayloadType, parameter: MessageParamsType, sandbox?: SinonSandbox) { - return getCommandContextMock( - payload, - parameter, - sandbox, - this.invokes, - this.emitList, - ) - } - - /** - * Returns a mocked transform function context, which can be used in unit tests. - * - * @param message - * @param sandbox Sinon sandbox - * @returns a mocked transform function context - */ - getCommandTransformContextMock(payload: MessagePayloadType, parameter: MessageParamsType, sandbox?: SinonSandbox) { - return getCommandTransformContextMock(payload, parameter, sandbox) - } + private httpMetadata?: HttpExposedServiceMeta + private inputSchema?: Schema + private inputContentType: ContentType | undefined + private inputContentEncoding: string | undefined + private outputSchema?: Schema + private outputContentType: ContentType | undefined + private outputContentEncoding: string | undefined + private parameterSchema?: Schema + private queryParameter: QueryParameter[] = [] + + private tags: string[] = [] + + private deprecated = false + + private summary?: string + + private errorStatusCodes: StatusCode[] = [] + + private isSecure = true + + private operationId?: string + + private durable = false + private autoacknowledge = true + + private invokes: C['Invokes'] = {} + + private emitList: C['EmitList'] = {} + + private hooks: { + transformInput?: { + transformInputSchema: Schema + transformParameterSchema: Schema + transformFunction: CommandTransformInputHook + } + beforeGuard: Record< + string, + CommandBeforeGuardHook + > + afterGuard: Record< + string, + CommandAfterGuardHook + > + transformOutput?: { + transformOutputSchema: Schema + transformFunction: CommandTransformOutputHook + } + } = { + transformInput: undefined, + beforeGuard: {}, + afterGuard: {}, + transformOutput: undefined, + } + + private fn?: CommandFunction + + constructor( + private commandName: Exclude, + private commandDescription: string, + private eventName?: Exclude, + deprecated = false, + ) { + this.deprecated = deprecated + } + + /** + * Define a command which can be invoked by the current command + * @param serviceName + * @param serviceVersion + * @param serviceTarget + * @param outputSchema + * @param payloadSchema + * @param parameterSchema + * @returns + */ + canInvoke< + Output extends Schema, + Payload extends Schema, + Parameter extends Schema, + SName extends string = string, + Version extends string = string, + Fname extends string = string, + >( + serviceName: SName, + serviceVersion: Version, + serviceTarget: Fname, + outputSchema?: Output, + payloadSchema?: Payload, + parameterSchema?: Parameter, + ) { + if (serviceName.trim() === '' || serviceVersion.trim() === '' || serviceTarget.trim() === '') { + throw new Error('canInvoke requires non-empty service name, version and target') + } + + const x = this.invokes as any + if (!x[serviceName]) { + x[serviceName] = {} + } + + if (!x[serviceName][serviceVersion]) { + x[serviceName][serviceVersion] = {} + } + + const f = { + [serviceName]: { + [serviceVersion]: { + [serviceTarget]: { outputSchema, payloadSchema, parameterSchema }, + }, + }, + } as unknown as C['Invokes'] & + Record< + SName, + Record< + Version, + Record, parameter: InferIn) => Promise>> + > + > + + this.invokes = { + ...this.invokes, + ...f, + } + + return this as unknown as CommandDefinitionBuilder< + S, + CommandDefinitionBuilderTypes< + C['PayloadSchema'], + C['ParamsSchema'], + C['OutputSchema'], + C['TransformInputPayloadSchema'], + C['TransformInputParamsSchema'], + C['TransformOutputSchema'], + C['Resources'], + C['Invokes'] & + Record< + SName, + Record< + Version, + Record, parameter: InferIn) => Promise>> + > + >, + C['EmitList'] + > + > + } + + /** + * Define which custom events the command can emit. + * + * @param eventName The custom event name + * @param schema the payload schema + * @returns + */ + canEmit(eventName: EventName, schema: T) { + if (eventName.trim() === '') { + throw new Error('canEmit requires non-empty event name') + } + + this.emitList = { ...this.emitList, [eventName]: schema } + + return this as unknown as CommandDefinitionBuilder< + S, + CommandDefinitionBuilderTypes< + C['PayloadSchema'], + C['ParamsSchema'], + C['OutputSchema'], + C['TransformInputPayloadSchema'], + C['TransformInputParamsSchema'], + C['TransformOutputSchema'], + C['Resources'], + C['Invokes'], + C['EmitList'] & Record> + > + > + } + + /* + * Event name of success response message. + * This makes it easy to subscribe to something like `UserCreated` (optional add service version to the subscription). + * Otherwise you will need to subscribe to service name, service function, and message type to get the message. + * It is also essential to have this possibility to be able to build event sourcing architectures. + */ + setSuccessEventName(eventName: NonEmptyString) { + this.eventName = eventName + return this + } + + /** + * Add a schema for input payload validation. + * Types for payload of message and function payload input are generated from given schema. + * @param inputSchema The schema validation for input payload + * @param inputContentType optional the content type of payload + * @param inputContentEncoding optional the content encoding + * @returns CommandDefinitionBuilder + */ + addPayloadSchema( + inputSchema: PayloadSchema, + inputContentType?: ContentType, + inputContentEncoding?: string, + ) { + this.inputContentType = inputContentType ?? this.inputContentType + this.inputContentEncoding = inputContentEncoding ?? this.inputContentEncoding + this.inputSchema = inputSchema + return this as unknown as CommandDefinitionBuilder< + S, + CommandDefinitionBuilderTypes< + PayloadSchema, + C['ParamsSchema'], + C['OutputSchema'], + C['TransformInputPayloadSchema'], + C['TransformInputParamsSchema'], + C['TransformOutputSchema'], + C['Resources'], + C['Invokes'], + C['EmitList'] + > + > + } + + /** + * Add a schema for output parameter validation. + * Types for parameter of message and function parameter output are generated from given schema. + * @param parameterSchema The schema validation for output parameter + * @returns CommandDefinitionBuilder + */ + addParameterSchema(parameterSchema: ParamsSchema) { + this.parameterSchema = parameterSchema + return this as unknown as CommandDefinitionBuilder< + S, + CommandDefinitionBuilderTypes< + C['PayloadSchema'], + ParamsSchema, + C['OutputSchema'], + C['TransformInputPayloadSchema'], + C['TransformInputParamsSchema'], + C['TransformOutputSchema'], + C['Resources'], + C['Invokes'], + C['EmitList'] + > + > + } + + /** + * Add a schema for output payload validation. + * Types for payload of message and function payload output are generated from given schema. + * @param outputSchema The schema validation for output payload + * @param outputContentType optional the content type of payload + * @param outputContentEncoding optional the content encoding + * @returns CommandDefinitionBuilder + */ + addOutputSchema( + outputSchema: OutputSchema, + outputContentType?: ContentType, + outputContentEncoding?: string, + ) { + this.outputContentType = outputContentType ?? this.outputContentType + this.outputContentEncoding = outputContentEncoding ?? this.outputContentEncoding + this.outputSchema = outputSchema + return this as unknown as CommandDefinitionBuilder< + S, + CommandDefinitionBuilderTypes< + C['PayloadSchema'], + C['ParamsSchema'], + OutputSchema, + C['TransformInputPayloadSchema'], + C['TransformInputParamsSchema'], + C['TransformOutputSchema'], + C['Resources'], + C['Invokes'], + C['EmitList'] + > + > + } + + /** + * Mark this endpoint/command as deprecated + * @returns CommandDefinitionBuilder + */ + markAsDeprecated() { + this.deprecated = true + return this + } + + /** + * Define query parameters if you expose the function as http endpoint. + * Query parameters are add to openApi definition. + * Query parameters are add to input parameters. + * + * @example + * ```ts + * .addQueryParameters( + * { + * required: false, + * name: 'search', + * }, + * { + * required: false, + * name: 'limit', + * }, + * ) + * ``` + * + * @param queryParams Add one or more query parameter definitions + * @returns CommandDefinitionBuilder + */ + addQueryParameters(...queryParams: QueryParameter>[]) { + this.queryParameter.push(...(queryParams as any)) + return this + } + + /** + * Add tags for openApi definition for given function. + * It is recommended to use some enum for tags to avoid typo issues. + * + * @example + * ```ts + * addTags('User','Public') + * ``` + * + * @param tags List of tag strings + * @returns CommandDefinitionBuilder + */ + addOpenApiTags(...tags: string[]) { + this.tags.push(...tags) + return this + } + + /** + * If a function can return other status codes, than the default ones, you should add them to openApi definition. + * Per default, 200, 204, 400, 401 and 500 can be autogenerated in most cases. + * Special cases or different status codes should be added with this function. + * + * @example + * ```ts + * addErrorStatusCodes(StatusCode.PaymentRequired, StatusCode.Conflict) + * ``` + * + * @param codes List of status codes + * @returns CommandDefinitionBuilder + */ + addOpenApiErrorStatusCodes(...codes: StatusCode[]) { + this.errorStatusCodes.push(...codes) + return this + } + + /** + * Set a transform input hook which will encode or transform the input payload and parameters. + * Will be executed as first step before input validation, before guard and the function itself. + * This will change the type of input message payload and input message parameter. + * @param transformInputSchema Input payload validation schema + * @param transformParameterSchema Input parameter validation schema + * @param transformFunction Transform input function + * @param inputContentType optional the content type of payload + * @param inputContentEncoding optional the content encoding + * @returns CommandDefinitionBuilder + */ + setTransformInput( + transformInputSchema: TransformInputPayloadSchema, + transformParameterSchema: TransformInputParamsSchema, + transformFunction: CommandTransformInputHook< + S, + InferIn, + InferIn, + Infer, + Infer, + InferIn, + InferIn, + C['Resources'] + >, + inputContentType?: ContentType, + inputContentEncoding?: string, + ) { + this.inputContentType = inputContentType ?? this.inputContentType + this.inputContentEncoding = inputContentEncoding ?? this.inputContentEncoding + + this.hooks.transformInput = { + transformFunction: transformFunction, + transformInputSchema, + transformParameterSchema, + } + return this as unknown as CommandDefinitionBuilder< + S, + CommandDefinitionBuilderTypes< + C['PayloadSchema'], + C['ParamsSchema'], + C['OutputSchema'], + TransformInputPayloadSchema, + TransformInputParamsSchema, + C['TransformOutputSchema'], + C['Resources'], + C['Invokes'], + C['EmitList'] + > + > + } + + /** + * Return the transform input function + * @returns the input transform function if defined + */ + getTransformInputFunction() { + if (!this.hooks.transformInput) { + return undefined + } + + return this.hooks.transformInput.transformFunction as CommandTransformInputHook< + S, + InferIn, + InferIn, + Infer, + Infer, + InferIn, + InferIn, + C['Resources'] + > + } + + /** + * Set a transform output hook which will encode or transform the response payload. + * Will be executed at very last step after function execution, output validation and after guard hooks. + * This will change the type of output message payload. + * @param transformOutputSchema The output validation schema + * @param transformFunction Transform output function + * @param outputContentType optional the content type of payload + * @param outputContentEncoding optional the content encoding + * @returns CommandDefinitionBuilder + */ + setTransformOutput( + transformOutputSchema: TransformOutputSchema, + transformFunction: CommandTransformOutputHook< + S, + GetMessagePayloadType, + GetMessageParamsType, + Infer, + Infer, + InferIn, + C['Resources'] + >, + + outputContentType?: ContentType, + outputContentEncoding?: string, + ) { + this.outputContentEncoding = outputContentEncoding ?? this.outputContentEncoding + this.outputContentType = outputContentType ?? this.outputContentType + this.hooks.transformOutput = { + transformFunction: transformFunction, + transformOutputSchema, + } + return this as unknown as CommandDefinitionBuilder< + S, + CommandDefinitionBuilderTypes< + C['PayloadSchema'], + C['ParamsSchema'], + C['OutputSchema'], + C['TransformInputPayloadSchema'], + C['TransformInputParamsSchema'], + TransformOutputSchema, + C['Resources'], + C['Invokes'], + C['EmitList'] + > + > + } + + /** + * Return the transform output function + * @returns the transform output function if defined + */ + getTransformOutputFunction() { + if (!this.hooks.transformOutput) { + return undefined + } + + return this.hooks.transformOutput.transformFunction as CommandTransformOutputHook< + S, + GetMessagePayloadType, + GetMessageParamsType, + Infer, + Infer, + InferIn, + C['Resources'] + > + } + + /** + * Set one or more before guard hook(s). + * If there are multiple before guard hooks, they are executed in parallel + * @param beforeGuards Object of key = name of guard, value = function + * @returns CommandDefinitionBuilder + */ + setBeforeGuardHooks( + beforeGuards: Record< + string, + CommandBeforeGuardHook< + S, + GetMessagePayloadType, + GetMessageParamsType, + Infer, + Infer, + C['Resources'], + C['Invokes'], + C['EmitList'] + > + >, + ) { + this.hooks.beforeGuard = { ...this.hooks.beforeGuard, ...beforeGuards } + return this + } + + /** + * Get the before guard hook for this command. + * + * @param name The name of the before guard to retrieve. + * @returns The before guard hook, or undefined if not found. + */ + getBeforeGuardHook(name: keyof typeof this.hooks.beforeGuard) { + this.hooks.beforeGuard[name] as CommandBeforeGuardHook< + S, + GetMessagePayloadType, + GetMessageParamsType, + Infer, + Infer, + C['Resources'], + C['Invokes'], + C['EmitList'] + > + } + + /** + * Set one or more after guard hook(s). + * If there are multiple after guard hooks, they are executed in parallel + * @param afterGuard Object of key = name of guard, value = function + * @returns CommandDefinitionBuilder + */ + setAfterGuardHooks( + afterGuards: Record< + string, + CommandAfterGuardHook< + S, + GetMessagePayloadType, + GetMessageParamsType, + Infer, + Infer, + Infer, + C['Resources'], + C['Invokes'], + C['EmitList'] + > + >, + ) { + this.hooks.afterGuard = { ...this.hooks.afterGuard, ...afterGuards } + return this + } + + /** + * Returns the after guard hook corresponding to the provided name. + * @param name - The name of the hook. + * @return The after guard hook, or undefined if not found. + */ + getAfterGuardHook(name: keyof typeof this.hooks.afterGuard) { + this.hooks.afterGuard[name] as CommandAfterGuardHook< + S, + GetMessagePayloadType, + GetMessageParamsType, + Infer, + Infer, + Infer, + C['Resources'], + C['Invokes'], + C['EmitList'] + > + } + + /** + * Mark the function to be exposed as http endpoint. + * + * Api url prefix and service version are prepended automatically + * + * For exposing a url like: `/api/V1/user/login` simply provide `user/login`as path + * + * @param method Http method POST, PUT, PATCH, GET, DELETE + * @param path The url path + * @param contentTypeRequest input content type defaults to application/json + * @param contentEncodingRequest input content encoding defaults to utf-8 + * @param contentTypeResponse input content type defaults to application/json + * @param contentEncodingResponse input content encoding defaults to utf-8 + * @returns CommandDefinitionBuilder + */ + exposeAsHttpEndpoint( + method: SupportedHttpMethod, + path: string, + contentTypeRequest?: ContentType, + contentEncodingRequest?: string, + contentTypeResponse?: ContentType, + contentEncodingResponse?: string, + ) { + this.httpMetadata = { + expose: { + contentTypeRequest: contentTypeRequest ?? this.inputContentType ?? 'application/json', + contentEncodingRequest: contentEncodingRequest ?? this.inputContentEncoding ?? 'utf-8', + contentTypeResponse: contentTypeResponse ?? this.outputContentType ?? 'application/json', + contentEncodingResponse: contentEncodingResponse ?? this.outputContentEncoding ?? 'utf-8', + http: { + method, + path, + }, + }, + } + return this + } + + /** + * enable or disable security for this endpoint + * @param enabled Defaults to true if not set means "enable security" + * @returns CommandDefinitionBuilder + */ + enableHttpSecurity(enabled = true) { + this.isSecure = enabled + return this + } + + /** + * enable or disable security for this endpoint + * @param enabled Defaults to true if not set meaning "disable security" + * @returns CommandDefinitionBuilder + * @deprecated use makeEndpointPublic() instead + */ + disableHttpSecurity(disabled = true) { + this.isSecure = !disabled + return this + } + + /** + * Mark the endpoint to be public available. + * No security check like bearer token or basic auth is required to access the endpoint. + * @returns CommandDefinitionBuilder + */ + makeEndpointPublic() { + this.isSecure = false + return this + } + + /** + * Set the function summary text used for example in openApi documentation + * + * @example + * ```ts + * setSummary('Some function summary') + * ``` + * + * @param summary Summary text + * @returns CommandDefinitionBuilder + */ + setOpenApiSummary(summary: string) { + this.summary = summary + return this + } + + /** + * Set the operationId for openApi documentation + * @param operationId + * @returns CommandDefinitionBuilder + */ + setOpenApiOperationId(operationId: string) { + this.operationId = operationId + return this + } + + private extendWithHttpMetadata< + MessagePayloadType, + MessageParamsType, + TransformInputPayload, + TransformInputParams, + FunctionPayloadType, + FunctionParamsType, + FunctionOutputType, + FinalFunctionOutputType, + TransformOutputHookOutput, + Resources extends Record, + Invokes extends InvokeList, + EmitList extends Record, + >( + definition: Complete< + CommandDefinition< + S, + MessagePayloadType, + MessageParamsType, + TransformInputPayload, + TransformInputParams, + FunctionPayloadType, + FunctionParamsType, + FunctionOutputType, + FinalFunctionOutputType, + TransformOutputHookOutput, + Resources, + Invokes, + EmitList, + CommandDefinitionMetadataBase + > + >, + ) { + if (!this.httpMetadata) { + return definition + } + + const def: Complete< + CommandDefinition< + S, + MessagePayloadType, + MessageParamsType, + TransformInputPayload, + TransformInputParams, + FunctionPayloadType, + FunctionParamsType, + FunctionOutputType, + FinalFunctionOutputType, + TransformOutputHookOutput, + Resources, + Invokes, + EmitList, + HttpExposedServiceMeta> + > + > = { + ...definition, + metadata: { + ...definition.metadata, + expose: { + ...definition.metadata?.expose, + ...this.httpMetadata.expose, + }, + }, + } + + def.metadata.expose.http.openApi = { + description: this.commandDescription, + summary: this.summary ?? this.commandName, + isSecure: this.isSecure, + query: this.queryParameter, + tags: this.tags, + additionalStatusCodes: this.errorStatusCodes, + operationId: this.operationId ?? this.commandName, + } + + return def + } + + /** + * Instruct the event bridge message broker to autoacknowledge commands as soon as they arrive. + * This means, a message will not be resent, if the command execution fails unexpected. + * + * If set to false, the command message will be resent from message broker to eventbridge, if: + * - the underlaying message broker supports it + * - if the command execution fails unexpected + * - if sending of command response failed + * + * @param acknowledge Enable (true) and disable (false) + * @returns CommandDefinition + */ + adviceAutoacknowledgeMessages(acknowledge = true) { + this.autoacknowledge = acknowledge + return this + } + + /** + * Creates and returns the CommandDefinition used as input for the service. + * @returns CommandDefinition + */ + async getDefinition() { + if (!this.fn) { + throw new Error('CommandDefinitionBuilder: missing function implementation') + } + + const eventName = this.eventName + + const inputPayloadSchema: Schema | undefined = this.hooks.transformInput?.transformInputSchema ?? this.inputSchema + const inputParameterSchema: Schema | undefined = + this.hooks.transformInput?.transformParameterSchema ?? this.parameterSchema + const outputPayloadSchema: Schema | undefined = + this.hooks.transformOutput?.transformOutputSchema ?? this.outputSchema + + const eventBridgeConfig: Complete = { + durable: this.durable, + autoacknowledge: this.autoacknowledge, + shared: true, + } + + const [inputPayload, parameter, outputPayload, invokes, emitList] = await Promise.all([ + validationToSchema(inputPayloadSchema), + validationToSchema(inputParameterSchema), + validationToSchema(outputPayloadSchema), + convertInvokeValidationsToSchema(this.invokes), + convertEmitValidationsToSchema(this.emitList), + ]) + + const definition: Complete< + CommandDefinition< + S, + GetMessagePayloadType, + GetMessageParamsType, + Infer, + Infer, + Infer, + Infer, + InferIn, + Infer, + InferIn, + C['Resources'], + C['Invokes'], + C['EmitList'] + > + > = { + commandName: this.commandName, + commandDescription: this.commandDescription, + eventBridgeConfig, + metadata: { + expose: { + contentTypeRequest: this.inputContentType ?? 'application/json', + contentEncodingRequest: this.inputContentEncoding ?? 'utf-8', + contentTypeResponse: this.outputContentType ?? 'application/json', + contentEncodingResponse: this.outputContentEncoding ?? 'utf-8', + inputPayload, + parameter, + outputPayload, + deprecated: this.deprecated, + }, + }, + eventName, + call: this.getCommandFunction() as any, + hooks: this.hooks, + invokes, + emitList, + } + + return this.extendWithHttpMetadata(definition) + } + + /** + * Required: Set the function implementation. + * The types should be automatically set as soon as schemas previously defined. + * As the function will be a a function of a service class you need to implement as function declaration. + * Anonymous functions do not have access to the `this` scope. + * + * @example + * ```ts + * async function (context, payload, parameter) { + * + * return `the result output payload` + * } + * ``` + * @param fn the function implementation + * @returns CommandDefinitionBuilder + */ + public setCommandFunction( + fn: CommandFunction< + S, + GetMessagePayloadType, + GetMessageParamsType, + Infer, + Infer, + InferIn, + C['Resources'], + C['Invokes'], + C['EmitList'] + >, + ) { + this.fn = fn + + return this + } + + /** + * Get the function implementation including input and output validation. + * Also, before hooks are triggered during execution. + * Before guards can be optional overwritten by optional input parameter. + * + * + * @param input Overwrite beforeGuards + * @returns the function + */ + getCommandFunction(input?: { + beforeGuards?: Partial + }) { + if (!this.fn) { + throw new UnhandledError(StatusCode.NotImplemented, `No function implementation for ${this.commandName}`, { + commandName: this.commandName, + }) + } + + return getCommandFunctionWithValidation(this.fn, this.inputSchema, this.parameterSchema, this.outputSchema, { + ...this.hooks.beforeGuard, + ...input?.beforeGuards, + }) as CommandFunction< + S, + GetMessagePayloadType, + GetMessageParamsType, + InferIn, + InferIn, + InferIn, + C['Resources'], + C['Invokes'], + C['EmitList'] + > + } + + /** + * Get the function implementation without input and output validation. + * No hooks are triggered during execution. + * + * @returns the function + */ + getCommandFunctionPlain() { + if (!this.fn) { + throw new UnhandledError(StatusCode.NotImplemented, `No function implementation for ${this.commandName}`, { + commandName: this.commandName, + }) + } + + return this.fn as CommandFunction< + S, + GetMessagePayloadType, + GetMessageParamsType, + Infer, + Infer, + InferIn, + C['Resources'], + C['Invokes'], + C['EmitList'] + > + } + + /** + * + * @param input + * @returns a mocked command context + */ + getCommandContextMock< + MessagePayloadType = GetMessagePayloadType, + MessageParamsType = GetMessageParamsType, + FunctionPayloadType = InferIn, + FunctionParamsType = InferIn, + >(input: { + payload: FunctionPayloadType + parameter: FunctionParamsType + resources?: Partial + sandbox?: SinonSandbox + message?: { + payload: MessagePayloadType + parameter: MessageParamsType + } + }) { + return getCommandContextMock< + MessagePayloadType, + MessageParamsType, + FunctionPayloadType, + FunctionParamsType, + C['Resources'], + C['Invokes'], + C['EmitList'] + >({ + ...input, + invokes: this.invokes, + emitList: this.emitList, + }) + } + + /** + * Returns a mocked transform function context, which can be used in unit tests. + * + * @param input + * @returns a mocked transform function context + */ + getCommandTransformContextMock(input: { + payload: GetMessagePayloadType + parameter: GetMessageParamsType + resources?: Partial + sandbox?: SinonSandbox + }) { + return getCommandTransformContextMock(input) + } } diff --git a/packages/core/src/CommandDefinitionBuilder/CommandDefinitionBuilderTypes.ts b/packages/core/src/CommandDefinitionBuilder/CommandDefinitionBuilderTypes.ts new file mode 100644 index 000000000..76c4526e6 --- /dev/null +++ b/packages/core/src/CommandDefinitionBuilder/CommandDefinitionBuilderTypes.ts @@ -0,0 +1,24 @@ +import type { Schema } from '@typeschema/main' +import type { EmptyObject, InvokeList } from '../core/index.js' + +export type CommandDefinitionBuilderTypes< + PayloadSchema extends Schema = Schema, + ParamsSchema extends Schema = Schema, + OutputSchema extends Schema = Schema, + TransformInputPayloadSchema extends Schema = Schema, + TransformInputParamsSchema extends Schema = Schema, + TransformOutputSchema extends Schema = Schema, + Resources extends Record = EmptyObject, + Invokes extends InvokeList = InvokeList, + EmitList extends Record = Record, +> = { + PayloadSchema: PayloadSchema + ParamsSchema: ParamsSchema + OutputSchema: OutputSchema + TransformInputPayloadSchema: TransformInputPayloadSchema + TransformInputParamsSchema: TransformInputParamsSchema + TransformOutputSchema: TransformOutputSchema + Resources: Resources + Invokes: Invokes + EmitList: EmitList +} diff --git a/packages/core/src/CommandDefinitionBuilder/commandDefinitionBuilder.test.ts b/packages/core/src/CommandDefinitionBuilder/commandDefinitionBuilder.test.ts index 20526012a..fe06e1296 100644 --- a/packages/core/src/CommandDefinitionBuilder/commandDefinitionBuilder.test.ts +++ b/packages/core/src/CommandDefinitionBuilder/commandDefinitionBuilder.test.ts @@ -7,298 +7,312 @@ import { getEventBridgeMock, getLoggerMock } from '../mocks/index.js' import { CommandDefinitionBuilder } from './CommandDefinitionBuilder.impl.js' describe('CommandDefinitionBuilder', () => { - const sandbox = createSandbox() - const service = new Service({ - info: { serviceName: 'TestService', serviceVersion: '1', serviceDescription: 'A service' }, - commandDefinitionList: [], - subscriptionDefinitionList: [], - logger: getLoggerMock(sandbox).mock, - eventBridge: getEventBridgeMock(sandbox).mock, - config: {}, - }) - - const functionPayloadSchema = z.object({ foo: z.string(), bar: z.number(), def: z.string().default('default_value') }) - const functionParameterSchema = z.object({ - paramOne: z.string(), - paramTwo: z.number(), - def: z.string().default('default_param'), - }) - const functionOutputSchema = z.object({ - result: z.object({ - payload: z.object({ foo: z.string(), bar: z.number(), other: z.string(), def: z.string() }), - parameter: z.object({ paramOne: z.string(), paramTwo: z.number(), def: z.string() }), - }), - }) - const transformPayloudSchema = z.string() - const transformParameterSchema = z.string() - const transformOutputSchema = z.string() - - const beforeOneStub = sandbox.stub() - const afterOneStub = sandbox.stub() - - const builder = new CommandDefinitionBuilder('testCommand', 'a unit test command') - .addPayloadSchema(functionPayloadSchema) - .addParameterSchema(functionParameterSchema) - .addOutputSchema(functionOutputSchema) - .setTransformInput(transformPayloudSchema, transformParameterSchema, async (_context, payload, parameter) => { - expect(typeof payload).toBe('string') - expect(typeof parameter).toBe('string') - - const pay: { - foo: string - bar: number - } = JSON.parse(payload) - const param: { - paramOne: string - paramTwo: number - } = JSON.parse(parameter) - - return { - payload: pay, - parameter: param, - } - }) - .setTransformOutput(transformOutputSchema, async (_context, payload, _parameter) => { - const p: Readonly<{ - result: { - payload: { - foo: string - bar: number - def: string - other: string - } - parameter: { - paramOne: string - paramTwo: number - } - } - }> = payload - return JSON.stringify(p) - }) - .setBeforeGuardHooks({ - beforeOne: async (_context, payload, parameter) => { - const pay: { - foo: string - bar: number - def: string - } = payload - - const param: { - def: string - paramOne: string - paramTwo: number - } = parameter - beforeOneStub(pay, param) - }, - }) - .setAfterGuardHooks({ - afterOne: async (_context, fnOutputPayload, input, parameter) => { - const pay: { - foo: string - bar: number - def: string - } = input - - const param: { - def: string - paramOne: string - paramTwo: number - } = parameter - - const fnRes: { - result: { - payload: { - foo: string - bar: number - def: string - other: string - } - parameter: { - def: string - paramOne: string - paramTwo: number - } - } - } = fnOutputPayload - - afterOneStub(fnRes, pay, param) - }, - }) - .canInvoke( - 'OtherService', - '2', - 'testCommand', - functionOutputSchema.merge(z.object({ toBeRemovedInResponse: z.string() })), - functionPayloadSchema, - functionParameterSchema, - ) - .canEmit('some', z.object({ example: z.string() })) - .setCommandFunction(async (context, payload, parameter) => { - const result = await context.service.OtherService[2].testCommand(payload, parameter) - - const response: { - result: { - payload: { - foo: string - bar: number - def: string - other: string - } - parameter: { - def: string - paramOne: string - paramTwo: number - } - } - toBeRemovedInResponse: string - } = result - - context.emit('some', { example: 'test' }) - - return response - }) - - const payload = { - foo: 'foo', - bar: 1, - } - const parameter = { - paramOne: 'Parameter 1', - paramTwo: 2, - } - - beforeEach(() => { - sandbox.reset() - }) - - afterAll(() => { - sandbox.restore() - }) - - it('can build a command with schemas', async () => { - const commandFunction = safeBind(builder.getCommandFunction(), service) - const context = builder.getCommandContextMock(JSON.stringify(payload), JSON.stringify(parameter)) - context.stubs.service.OtherService[2].testCommand.callsFake(async (payload, parameter) => { - return { - result: { - payload: { ...payload, other: 'added by invoke' }, - parameter, - }, - } - }) - - const result = await commandFunction(context.mock, payload, parameter) - - expect(result).toStrictEqual({ - result: { - payload: { ...payload, other: 'added by invoke', def: 'default_value' }, - parameter: { ...parameter, def: 'default_param' }, - }, - }) - - expect(context.stubs.emit['some'].called).toBeTruthy() - expect(beforeOneStub.callCount).toBe(1) - }) - - it('executes the plain function without hooks and schema validation', async () => { - const commandFunction = safeBind(builder.getCommandFunctionPlain(), service) - const context = builder.getCommandContextMock(JSON.stringify(payload), JSON.stringify(parameter)) - context.stubs.service.OtherService[2].testCommand.callsFake(async (payload, parameter) => { - return { - result: { - payload: { ...payload, other: 'added by invoke' }, - parameter, - toBeRemovedInResponse: 'removed by output schema', - }, - } - }) - - const result = await commandFunction( - context.mock, - { ...payload, def: 'default_value' }, - { ...parameter, def: 'default_param' }, - ) - - expect(result).toStrictEqual({ - result: { - payload: { ...payload, other: 'added by invoke', def: 'default_value' }, - parameter: { ...parameter, def: 'default_param' }, - toBeRemovedInResponse: 'removed by output schema', - }, - }) - - expect(context.stubs.emit['some'].called).toBeTruthy() - expect(beforeOneStub.callCount).toBe(0) - }) - - it('does not throw on transform input', async () => { - const fn = builder.getTransformInputFunction() - - if (!fn) { - expect(fn).toBeDefined() - return - } - - const transformFunction = safeBind(fn, service) - - const context = builder.getCommandTransformContextMock(JSON.stringify(payload), JSON.stringify(parameter), sandbox) - - const result = await transformFunction(context.mock, JSON.stringify(payload), JSON.stringify(parameter)) - - expect(result).toStrictEqual({ payload, parameter }) - }) - - it('does not throw on transform output', async () => { - const fn = builder.getTransformOutputFunction() - - if (!fn) { - expect(fn).toBeDefined() - return - } - - const transformFunction = safeBind(fn, service) - - const context = builder.getCommandTransformContextMock(JSON.stringify(payload), JSON.stringify(parameter), sandbox) - - const result = await transformFunction( - context.mock, - { - result: { - payload: { ...payload, other: 'added by invoke', def: 'default_value' }, - parameter: { ...parameter, def: 'default_param' }, - }, - }, - { ...parameter, def: 'default_param' }, - ) - - expect(result).toStrictEqual( - JSON.stringify({ - result: { - payload: { ...payload, other: 'added by invoke', def: 'default_value' }, - parameter: { ...parameter, def: 'default_param' }, - }, - }), - ) - }) - - it('works with without schema', async () => { - const b = new CommandDefinitionBuilder('testCommand', 'a unit test command') - - b.setCommandFunction(async (context, payload, parameter) => { - return { payload, parameter } - }) - - const fn = b.getCommandFunction() - - const theFunction = safeBind(fn, service) - - const context = b.getCommandContextMock('', {}, sandbox) - - const result = await theFunction(context.mock, 'y', 'x') - - expect(result).toStrictEqual({ - payload: 'y', - parameter: 'x', - }) - }) + const sandbox = createSandbox() + const service = new Service({ + info: { serviceName: 'TestService', serviceVersion: '1', serviceDescription: 'A service' }, + commandDefinitionList: [], + subscriptionDefinitionList: [], + logger: getLoggerMock(sandbox).mock, + eventBridge: getEventBridgeMock(sandbox).mock, + config: {}, + }) + + const functionPayloadSchema = z.object({ foo: z.string(), bar: z.number(), def: z.string().default('default_value') }) + const functionParameterSchema = z.object({ + paramOne: z.string(), + paramTwo: z.number(), + def: z.string().default('default_param'), + }) + const functionOutputSchema = z.object({ + result: z.object({ + payload: z.object({ foo: z.string(), bar: z.number(), other: z.string(), def: z.string() }), + parameter: z.object({ paramOne: z.string(), paramTwo: z.number(), def: z.string() }), + }), + }) + const transformPayloudSchema = z.string() + const transformParameterSchema = z.string() + const transformOutputSchema = z.string() + + const beforeOneStub = sandbox.stub() + const afterOneStub = sandbox.stub() + + const builder = new CommandDefinitionBuilder('testCommand', 'a unit test command') + .addPayloadSchema(functionPayloadSchema) + .addParameterSchema(functionParameterSchema) + .addOutputSchema(functionOutputSchema) + .setTransformInput(transformPayloudSchema, transformParameterSchema, async (_context, payload, parameter) => { + expect(typeof payload).toBe('string') + expect(typeof parameter).toBe('string') + + const pay: { + foo: string + bar: number + } = JSON.parse(payload) + const param: { + paramOne: string + paramTwo: number + } = JSON.parse(parameter) + + return { + payload: pay, + parameter: param, + } + }) + .setTransformOutput(transformOutputSchema, async (_context, payload, _parameter) => { + const p: Readonly<{ + result: { + payload: { + foo: string + bar: number + def: string + other: string + } + parameter: { + paramOne: string + paramTwo: number + } + } + }> = payload + return JSON.stringify(p) + }) + .setBeforeGuardHooks({ + beforeOne: async (_context, payload, parameter) => { + const pay: { + foo: string + bar: number + def: string + } = payload + + const param: { + def: string + paramOne: string + paramTwo: number + } = parameter + beforeOneStub(pay, param) + }, + }) + .setAfterGuardHooks({ + afterOne: async (_context, fnOutputPayload, input, parameter) => { + const pay: { + foo: string + bar: number + def: string + } = input + + const param: { + def: string + paramOne: string + paramTwo: number + } = parameter + + const fnRes: { + result: { + payload: { + foo: string + bar: number + def: string + other: string + } + parameter: { + def: string + paramOne: string + paramTwo: number + } + } + } = fnOutputPayload + + afterOneStub(fnRes, pay, param) + }, + }) + .canInvoke( + 'OtherService', + '2', + 'testCommand', + functionOutputSchema.merge(z.object({ toBeRemovedInResponse: z.string() })), + functionPayloadSchema, + functionParameterSchema, + ) + .canEmit('some', z.object({ example: z.string() })) + .setCommandFunction(async (context, payload, parameter) => { + const result = await context.service.OtherService[2].testCommand(payload, parameter) + + const response: { + result: { + payload: { + foo: string + bar: number + def: string + other: string + } + parameter: { + def: string + paramOne: string + paramTwo: number + } + } + toBeRemovedInResponse: string + } = result + + context.emit('some', { example: 'test' }) + + return response + }) + + const payload = { + foo: 'foo', + bar: 1, + } + const parameter = { + paramOne: 'Parameter 1', + paramTwo: 2, + } + + beforeEach(() => { + sandbox.reset() + }) + + afterAll(() => { + sandbox.restore() + }) + + it('can build a command with schemas', async () => { + const commandFunction = safeBind(builder.getCommandFunction(), service) + const context = builder.getCommandContextMock({ + payload: JSON.stringify(payload), + parameter: JSON.stringify(parameter), + }) + context.stubs.service.OtherService[2].testCommand.callsFake(async (payload, parameter) => { + return { + result: { + payload: { ...payload, other: 'added by invoke' }, + parameter, + }, + } + }) + + const result = await commandFunction(context.mock, payload, parameter) + + expect(result).toStrictEqual({ + result: { + payload: { ...payload, other: 'added by invoke', def: 'default_value' }, + parameter: { ...parameter, def: 'default_param' }, + }, + }) + + expect(context.stubs.emit.some.called).toBeTruthy() + expect(beforeOneStub.callCount).toBe(1) + }) + + it('executes the plain function without hooks and schema validation', async () => { + const commandFunction = safeBind(builder.getCommandFunctionPlain(), service) + const context = builder.getCommandContextMock({ + payload: JSON.stringify(payload), + parameter: JSON.stringify(parameter), + }) + context.stubs.service.OtherService[2].testCommand.callsFake(async (payload, parameter) => { + return { + result: { + payload: { ...payload, other: 'added by invoke' }, + parameter, + toBeRemovedInResponse: 'removed by output schema', + }, + } + }) + + const result = await commandFunction( + context.mock, + { ...payload, def: 'default_value' }, + { ...parameter, def: 'default_param' }, + ) + + expect(result).toStrictEqual({ + result: { + payload: { ...payload, other: 'added by invoke', def: 'default_value' }, + parameter: { ...parameter, def: 'default_param' }, + toBeRemovedInResponse: 'removed by output schema', + }, + }) + + expect(context.stubs.emit.some.called).toBeTruthy() + expect(beforeOneStub.callCount).toBe(0) + }) + + it('does not throw on transform input', async () => { + const fn = builder.getTransformInputFunction() + + if (!fn) { + expect(fn).toBeDefined() + return + } + + const transformFunction = safeBind(fn, service) + + const context = builder.getCommandTransformContextMock({ + payload: JSON.stringify(payload), + parameter: JSON.stringify(parameter), + sandbox, + }) + + const result = await transformFunction(context.mock, JSON.stringify(payload), JSON.stringify(parameter)) + + expect(result).toStrictEqual({ payload, parameter }) + }) + + it('does not throw on transform output', async () => { + const fn = builder.getTransformOutputFunction() + + if (!fn) { + expect(fn).toBeDefined() + return + } + + const transformFunction = safeBind(fn, service) + + const context = builder.getCommandTransformContextMock({ + payload: JSON.stringify(payload), + parameter: JSON.stringify(parameter), + sandbox, + }) + + const result = await transformFunction( + context.mock, + { + result: { + payload: { ...payload, other: 'added by invoke', def: 'default_value' }, + parameter: { ...parameter, def: 'default_param' }, + }, + }, + { ...parameter, def: 'default_param' }, + ) + + expect(result).toStrictEqual( + JSON.stringify({ + result: { + payload: { ...payload, other: 'added by invoke', def: 'default_value' }, + parameter: { ...parameter, def: 'default_param' }, + }, + }), + ) + }) + + it('works with without schema', async () => { + const b = new CommandDefinitionBuilder('testCommand', 'a unit test command').setCommandFunction( + async (_context, payload, parameter) => { + return { payload, parameter } + }, + ) + + const fn = b.getCommandFunction() + + const theFunction = safeBind(fn, service) + + const context = b.getCommandContextMock({ payload: '', parameter: {}, sandbox }) + + const result = await theFunction(context.mock, 'y', 'x') + + expect(result).toStrictEqual({ + payload: 'y', + parameter: 'x', + }) + }) }) diff --git a/packages/core/src/CommandDefinitionBuilder/getCommandFunctionWithValidation.impl.ts b/packages/core/src/CommandDefinitionBuilder/getCommandFunctionWithValidation.impl.ts index 3049aea9e..d95de8115 100644 --- a/packages/core/src/CommandDefinitionBuilder/getCommandFunctionWithValidation.impl.ts +++ b/packages/core/src/CommandDefinitionBuilder/getCommandFunctionWithValidation.impl.ts @@ -1,151 +1,116 @@ import { SpanStatusCode } from '@opentelemetry/api' -import type { Schema } from '@typeschema/main' -import { validate } from '@typeschema/main' +import { type Schema, validate } from '@typeschema/main' -import type { CommandBeforeGuardHook, CommandFunction, ServiceClass } from '../core/index.js' +import type { CommandBeforeGuardHook, CommandFunction, CommandFunctionContext, Service } from '../core/index.js' import { HandledError, StatusCode, UnhandledError } from '../core/index.js' -export const getCommandFunctionWithValidation = function < - ServiceClassType extends ServiceClass, - MessagePayloadType = unknown, - MessageParamsType = unknown, - MessageResultType = unknown, - FunctionPayloadType = MessagePayloadType, - FunctionParamsType = MessageParamsType, - FunctionResultType = MessageResultType, - Invokes = {}, - EmitListType = {}, ->( - fn: CommandFunction< - ServiceClassType, - MessagePayloadType, - MessageParamsType, - FunctionPayloadType, - FunctionParamsType, - FunctionResultType, - Invokes, - EmitListType - >, - inputPayloadSchema: Schema | undefined, - inputParameterSchema: Schema | undefined, - outputPayloadSchema: Schema | undefined, - beforeGuards: Record< - string, - CommandBeforeGuardHook< - ServiceClassType, - MessagePayloadType, - MessageParamsType, - FunctionPayloadType, - FunctionParamsType, - Invokes, - EmitListType - > - > = {}, -): CommandFunction< - ServiceClassType, - MessagePayloadType, - MessageParamsType, - FunctionPayloadType, - FunctionParamsType, - FunctionResultType, - Invokes, - EmitListType -> { - const wrapped: CommandFunction< - ServiceClassType, - MessagePayloadType, - MessageParamsType, - FunctionPayloadType, - FunctionParamsType, - FunctionResultType, - Invokes, - EmitListType - > = async function (context, payload, parameter): Promise { - const { logger, startActiveSpan, wrapInSpan } = context - let safePayload = payload as unknown as FunctionPayloadType - if (inputPayloadSchema) { - safePayload = await startActiveSpan('validatePayload', {}, undefined, async (span) => { - const validationResult = await validate(inputPayloadSchema, payload) - if (validationResult.success) { - return validationResult.data as FunctionPayloadType - } - const err = new HandledError( - StatusCode.BadRequest, - 'input validation for payload failed:', - validationResult.issues, - ) - span.recordException(err) - span.setStatus({ - code: SpanStatusCode.ERROR, - message: err.message, - }) - logger.warn({ ...span.spanContext() }, 'input validation for payload failed', err.message) - throw err - }) - } +export const getCommandFunctionWithValidation = function ( + fn: CommandFunction, + inputPayloadSchema: Schema | undefined, + inputParameterSchema: Schema | undefined, + outputPayloadSchema: Schema | undefined, + beforeGuards: Record>, +) { + const wrapped = async function ( + this: S, + context: CommandFunctionContext, + payload: unknown, + parameter: unknown, + ): Promise { + const { logger, startActiveSpan, wrapInSpan } = context - let safeParams = parameter as unknown as FunctionParamsType - if (inputParameterSchema) { - safeParams = await startActiveSpan('validateParameter', {}, undefined, async (span) => { - const validationResult = await validate(inputParameterSchema, parameter) - if (validationResult.success) { - return validationResult.data as FunctionParamsType - } + const getPayloadValue = async (): Promise => { + if (!inputPayloadSchema) { + return payload + } - const err = new HandledError( - StatusCode.BadRequest, - 'input validation for parameter failed:', - validationResult.issues, - ) - span.recordException(err) - span.setStatus({ - code: SpanStatusCode.ERROR, - message: err.message, - }) - logger.warn({ ...span.spanContext() }, 'input validation for parameter failed', err.message) - throw err - }) - } + return startActiveSpan('validatePayload', {}, undefined, async span => { + const validationResult = await validate(inputPayloadSchema, payload) + if (validationResult.success) { + return validationResult.data + } + const err = new HandledError( + StatusCode.BadRequest, + 'input validation for payload failed:', + validationResult.issues, + ) + span.recordException(err) + span.setStatus({ + code: SpanStatusCode.ERROR, + message: err.message, + }) + logger.warn({ ...span.spanContext() }, 'input validation for payload failed', err.message) + throw err + }) + } - if (Object.keys(beforeGuards).length) { - await startActiveSpan('beforeGuardHooks', {}, undefined, async () => { - const guards: Promise[] = [] + const getParameterValue = async (): Promise => { + if (!inputParameterSchema) { + return parameter + } + return startActiveSpan('validateParameter', {}, undefined, async span => { + const validationResult = await validate(inputParameterSchema, parameter) + if (validationResult.success) { + return validationResult.data + } - for (const [name, hook] of Object.entries(beforeGuards)) { - const guardPromise = wrapInSpan('beforeGuardHook.' + name, {}, async (_subSpan) => { - return hook.bind(this, context, safePayload, safeParams)() - }) - guards.push(guardPromise) - } + const err = new HandledError( + StatusCode.BadRequest, + 'input validation for parameter failed:', + validationResult.issues, + ) + span.recordException(err) + span.setStatus({ + code: SpanStatusCode.ERROR, + message: err.message, + }) + logger.warn({ ...span.spanContext() }, 'input validation for parameter failed', err.message) + throw err + }) + } - await Promise.all(guards) - }) - } + const [safePayload, safeParams] = await Promise.all([getPayloadValue(), getParameterValue()]) - const output = await startActiveSpan('functionExecution', {}, undefined, async () => { - const call = fn.bind(this, context, safePayload, safeParams) - return call() - }) + if (Object.keys(beforeGuards).length) { + await startActiveSpan('beforeGuardHooks', {}, undefined, async () => { + const guards: Promise[] = [] - if (!outputPayloadSchema) { - return output - } + for (const [name, hook] of Object.entries(beforeGuards)) { + const guardPromise = wrapInSpan(`beforeGuardHook.${name}`, {}, async _subSpan => { + return hook.bind(this, context, safePayload, safeParams)() + }) + guards.push(guardPromise) + } - return await startActiveSpan('outputValidation', {}, undefined, async (span) => { - const validationResult = await validate(outputPayloadSchema, output) - if (validationResult.success) { - return validationResult.data as FunctionResultType - } + await Promise.all(guards) + }) + } - const err = new UnhandledError(StatusCode.InternalServerError, 'output validation failed') - span.recordException(err) - span.setStatus({ - code: SpanStatusCode.ERROR, - message: err.message, - }) - logger.warn({ ...span.spanContext() }, 'output validation failed', err.message) - throw err - }) - } - return wrapped + const output = await startActiveSpan('functionExecution', {}, undefined, async () => { + const call = fn.bind(this, context, safePayload, safeParams) + return call() + }) + + if (!outputPayloadSchema) { + return output + } + + return await startActiveSpan('outputValidation', {}, undefined, async span => { + const validationResult = await validate(outputPayloadSchema, output) + if (validationResult.success) { + return validationResult.data + } + + const err = new UnhandledError(StatusCode.InternalServerError, 'output validation failed') + span.recordException(err) + span.setStatus({ + code: SpanStatusCode.ERROR, + message: err.message, + }) + logger.warn({ ...span.spanContext() }, 'output validation failed', err.message) + throw err + }) + } + + return wrapped } diff --git a/packages/core/src/CommandDefinitionBuilder/getCommandFunctionWithValidation.test.ts b/packages/core/src/CommandDefinitionBuilder/getCommandFunctionWithValidation.test.ts index b90246ae5..aece13f17 100644 --- a/packages/core/src/CommandDefinitionBuilder/getCommandFunctionWithValidation.test.ts +++ b/packages/core/src/CommandDefinitionBuilder/getCommandFunctionWithValidation.test.ts @@ -1,6 +1,6 @@ describe('getCommandFunctionWithValidation', () => { - it.todo('throws if parameter schema validation fails') - it.todo('throws if input schema validation fails') - it.todo('returns output if no schema is defined') - it.todo('throws if output schema validation fails') + it.todo('throws if parameter schema validation fails') + it.todo('throws if input schema validation fails') + it.todo('returns output if no schema is defined') + it.todo('throws if output schema validation fails') }) diff --git a/packages/core/src/CommandDefinitionBuilder/index.ts b/packages/core/src/CommandDefinitionBuilder/index.ts index 1500a2211..3bf8527e8 100644 --- a/packages/core/src/CommandDefinitionBuilder/index.ts +++ b/packages/core/src/CommandDefinitionBuilder/index.ts @@ -1,2 +1,3 @@ export * from './CommandDefinitionBuilder.impl.js' export * from './getCommandFunctionWithValidation.impl.js' +export * from './CommandDefinitionBuilderTypes.js' diff --git a/packages/core/src/DefaultConfigStore/DefaultConfigStore.impl.ts b/packages/core/src/DefaultConfigStore/DefaultConfigStore.impl.ts index afe07bc03..806c26072 100644 --- a/packages/core/src/DefaultConfigStore/DefaultConfigStore.impl.ts +++ b/packages/core/src/DefaultConfigStore/DefaultConfigStore.impl.ts @@ -26,39 +26,40 @@ import type { DefaultConfigStoreConfig } from './types/index.js' * @group Store */ export class DefaultConfigStore extends ConfigStoreBaseClass implements ConfigStore { - private map = new Map() - constructor(config?: StoreBaseConfig) { - super('DefaultConfigStore', { ...config }) - if (config?.config) { - this.map = new Map(Object.entries(config.config)) - } - this.logger.warn( - 'Using the DefaultConfigStore is not secure! It should only be used for test or development purpose.', - ) - } + private map = new Map() + constructor(config?: StoreBaseConfig) { + super('DefaultConfigStore', { ...config }) + if (config?.config) { + this.map = new Map(Object.entries(config.config)) + } + this.logger.warn( + 'Using the DefaultConfigStore is not secure! It should only be used for test or development purpose.', + ) + } - protected async getConfigImpl( - ...configNames: ConfigNames - ): Promise> { - if (!this.config.enableGet) { - throw new UnhandledError(StatusCode.Unauthorized, 'get config from store is disabled by config') - } + protected async getConfigImpl( + ...configNames: ConfigNames + ): Promise> { + if (!this.config.enableGet) { + throw new UnhandledError(StatusCode.Unauthorized, 'get config from store is disabled by config') + } - const result = configNames.reduce((prev, current) => { - return { - ...prev, - [current]: this.map.get(current), - } - }, {}) as ObjectWithKeysFromStringArray + const result = configNames.reduce((prev, current) => { + return { + // biome-ignore lint/performance/noAccumulatingSpread: + ...prev, + [current]: this.map.get(current), + } + }, {}) as ObjectWithKeysFromStringArray - return result - } + return result + } - protected async setConfigImpl(configName: string, configValue: unknown) { - this.map.set(configName, configValue) - } + protected async setConfigImpl(configName: string, configValue: unknown) { + this.map.set(configName, configValue) + } - protected async removeConfigImpl(configName: string) { - this.map.delete(configName) - } + protected async removeConfigImpl(configName: string) { + this.map.delete(configName) + } } diff --git a/packages/core/src/DefaultConfigStore/defaultConfigStore.test.ts b/packages/core/src/DefaultConfigStore/defaultConfigStore.test.ts index 2522be511..ce72830cf 100644 --- a/packages/core/src/DefaultConfigStore/defaultConfigStore.test.ts +++ b/packages/core/src/DefaultConfigStore/defaultConfigStore.test.ts @@ -4,61 +4,61 @@ import { getLoggerMock } from '../mocks/index.js' import { DefaultConfigStore } from './DefaultConfigStore.impl.js' describe('DefaultConfigStore', () => { - const sandbox = createSandbox() + const sandbox = createSandbox() - afterEach(() => { - sandbox.restore() - }) + afterEach(() => { + sandbox.restore() + }) - it('throws if operation is disabled', async () => { - const logger = getLoggerMock(sandbox) - const store = new DefaultConfigStore({ - logger: logger.mock, - enableGet: false, - enableRemove: false, - enableSet: false, - }) + it('throws if operation is disabled', async () => { + const logger = getLoggerMock(sandbox) + const store = new DefaultConfigStore({ + logger: logger.mock, + enableGet: false, + enableRemove: false, + enableSet: false, + }) - await expect(store.getConfig('example')).rejects.toThrow('get config from store is disabled by config') + await expect(store.getConfig('example')).rejects.toThrow('get config from store is disabled by config') - await expect(store.removeConfig('example')).rejects.toThrow('remove config from store is disabled by config') + await expect(store.removeConfig('example')).rejects.toThrow('remove config from store is disabled by config') - await expect(store.setConfig('example', 'value')).rejects.toThrow('set config at store is disabled by config') + await expect(store.setConfig('example', 'value')).rejects.toThrow('set config at store is disabled by config') - expect( - logger.stubs.warn.calledWith( - 'Using the DefaultConfigStore is not secure! It should only be used for test or development purpose.', - ), - ).toBeTruthy() - }) + expect( + logger.stubs.warn.calledWith( + 'Using the DefaultConfigStore is not secure! It should only be used for test or development purpose.', + ), + ).toBeTruthy() + }) - it('handles configs', async () => { - const logger = getLoggerMock(sandbox) - const store = new DefaultConfigStore({ - logger: logger.mock, - enableGet: true, - enableRemove: true, - enableSet: true, - config: { - initialConfig: 'initial', - }, - }) + it('handles configs', async () => { + const logger = getLoggerMock(sandbox) + const store = new DefaultConfigStore({ + logger: logger.mock, + enableGet: true, + enableRemove: true, + enableSet: true, + config: { + initialConfig: 'initial', + }, + }) - await expect(store.getConfig('initialConfig', 'unknownConfig')).resolves.toEqual({ - initialConfig: 'initial', - unknownConfig: undefined, - }) + await expect(store.getConfig('initialConfig', 'unknownConfig')).resolves.toEqual({ + initialConfig: 'initial', + unknownConfig: undefined, + }) - await expect(store.setConfig('initialConfig', 'other_value')).resolves.toBeUndefined() + await expect(store.setConfig('initialConfig', 'other_value')).resolves.toBeUndefined() - await expect(store.getConfig('initialConfig')).resolves.toEqual({ - initialConfig: 'other_value', - }) + await expect(store.getConfig('initialConfig')).resolves.toEqual({ + initialConfig: 'other_value', + }) - await expect(store.removeConfig('initialConfig')).resolves.toBeUndefined() + await expect(store.removeConfig('initialConfig')).resolves.toBeUndefined() - await expect(store.getConfig('initialConfig')).resolves.toEqual({ - initialConfig: undefined, - }) - }) + await expect(store.getConfig('initialConfig')).resolves.toEqual({ + initialConfig: undefined, + }) + }) }) diff --git a/packages/core/src/DefaultConfigStore/initDefaultConfigStore.impl.ts b/packages/core/src/DefaultConfigStore/initDefaultConfigStore.impl.ts index b302fa520..c9b7de6f9 100644 --- a/packages/core/src/DefaultConfigStore/initDefaultConfigStore.impl.ts +++ b/packages/core/src/DefaultConfigStore/initDefaultConfigStore.impl.ts @@ -2,6 +2,6 @@ import type { Logger } from '../core/index.js' import { DefaultConfigStore } from './DefaultConfigStore.impl.js' export const initDefaultConfigStore = (options: { logger: Logger }): DefaultConfigStore => { - const store = new DefaultConfigStore(options) - return store + const store = new DefaultConfigStore(options) + return store } diff --git a/packages/core/src/DefaultEventBridge/DefaultEventBridge.impl.ts b/packages/core/src/DefaultEventBridge/DefaultEventBridge.impl.ts index dbc06f9f0..39358f5ec 100644 --- a/packages/core/src/DefaultEventBridge/DefaultEventBridge.impl.ts +++ b/packages/core/src/DefaultEventBridge/DefaultEventBridge.impl.ts @@ -3,41 +3,41 @@ import { Stream } from 'node:stream' import { SpanKind, SpanStatusCode } from '@opentelemetry/api' import type { - Command, - CommandDefinitionMetadataBase, - CommandErrorResponse, - CommandSuccessResponse, - CustomMessage, - EBMessage, - EBMessageAddress, - EBMessageId, - EventBridge, - EventBridgeConfig, - Subscription, + Command, + CommandDefinitionMetadataBase, + CommandErrorResponse, + CommandSuccessResponse, + CustomMessage, + EBMessage, + EBMessageAddress, + EBMessageId, + EventBridge, + EventBridgeConfig, + Subscription, } from '../core/index.js' import { - createErrorResponse, - createInfoMessage, - deserializeOtp, - EBMessageType, - EventBridgeBaseClass, - EventBridgeEventNames, - getCleanedMessage, - getCommandQueueName, - getNewCorrelationId, - getNewEBMessageId, - getSubscriptionQueueName, - HandledError, - isCommand, - isCommandErrorResponse, - isCommandResponse, - isCommandSuccessResponse, - isInfoMessage, - PuristaSpanName, - PuristaSpanTag, - serializeOtp, - StatusCode, - UnhandledError, + EBMessageType, + EventBridgeBaseClass, + EventBridgeEventNames, + HandledError, + PuristaSpanName, + PuristaSpanTag, + StatusCode, + UnhandledError, + createErrorResponse, + createInfoMessage, + deserializeOtp, + getCleanedMessage, + getCommandQueueName, + getNewCorrelationId, + getNewEBMessageId, + getSubscriptionQueueName, + isCommand, + isCommandErrorResponse, + isCommandResponse, + isCommandSuccessResponse, + isInfoMessage, + serializeOtp, } from '../core/index.js' import { puristaVersion } from '../version.js' import { getDefaultEventBridgeConfig } from './getDefaultEventBridgeConfig.impl.js' @@ -61,360 +61,362 @@ import type { DefaultEventBridgeConfig, PendigInvocation, SubscriptionStorageEnt * @group Event bridge */ export class DefaultEventBridge extends EventBridgeBaseClass implements EventBridge { - protected writeStream = new Stream.Writable({ objectMode: true }) - protected readStream = new Stream.Readable({ - objectMode: true, - read() { - /* nothing to do here */ - }, - }) - - protected serviceFunctions = new Map< - string, - (message: Command) => Promise - >() - - protected pendingInvocations = new Map() - protected runningSubscriptionCount = 0 - - protected subscriptions = new Map() - - protected hasStarted = false - protected healthy = false - - constructor(config?: EventBridgeConfig) { - const conf = { - ...getDefaultEventBridgeConfig(), - logWarnOnMessagesWithoutReceiver: true, - ...config, - } - super('DefaultEventBridge', conf) - } - - async isReady() { - return this.hasStarted - } - - async isHealthy() { - return this.hasStarted - } - - async start() { - await super.start() - const write = async (message: Readonly, _encoding: string, next: (error?: Error) => void) => { - const context = deserializeOtp(this.logger, message.otp) - - return this.startActiveSpan( - PuristaSpanName.EventBridgeHandleIncomingMessage, - { kind: SpanKind.CONSUMER }, - context, - async (span) => { - try { - let isAtLeastDeliveredOnce = false - this.subscriptions.forEach((subscription) => { - if (isMessageMatchingSubscription(this.logger, message, subscription)) { - isAtLeastDeliveredOnce = true - this.runningSubscriptionCount++ - subscription - .cb(message) - .then((result) => { - if (subscription.emitEventName && result) { - this.emitMessage(result) - } - }) - .catch((err) => this.logger.error({ err })) - .finally(() => this.runningSubscriptionCount--) - } - }) - - if (isCommand(message)) { - const mapEntry = this.serviceFunctions.get(getCommandQueueName(message.receiver)) - if (!mapEntry) { - const err = new UnhandledError( - StatusCode.BadGateway, - 'InvalidCommand: received invalid command', - getCleanedMessage(message), - ) - span.setStatus({ - code: SpanStatusCode.ERROR, - message: err.message, - }) - span.recordException(err) - this.logger.error({ err, ...span.spanContext(), customTraceId: message.traceId }, err.message) - this.emit(EventBridgeEventNames.EventbridgeError, err) - - const errorResponse = createErrorResponse(this.instanceId, message, StatusCode.BadGateway, err) - this.emitMessage(errorResponse) - return next() - } - - isAtLeastDeliveredOnce = true - mapEntry(message as Readonly).then((result) => { - this.emitMessage(result) - }) - return next() - } - - if (isCommandResponse(message)) { - const mapEntry = this.pendingInvocations.get(message.correlationId) - if (!mapEntry) { - const err = new UnhandledError( - StatusCode.BadGateway, - 'InvalidCommandResponse: received invalid command response', - getCleanedMessage(message), - ) - span.setStatus({ - code: SpanStatusCode.ERROR, - message: err.message, - }) - span.recordException(err) - this.logger.error({ err, ...span.spanContext(), customTraceId: message.traceId }, err.message) - this.emit(EventBridgeEventNames.EventbridgeError, err) - return next() - } - - isAtLeastDeliveredOnce = true - if (isCommandSuccessResponse(message)) { - mapEntry.resolve(message.payload) - } else if (isCommandErrorResponse(message)) { - const error = message.isHandledError - ? HandledError.fromMessage(message) - : UnhandledError.fromMessage(message) - mapEntry.reject(error) - } - return next() - } - - if (isInfoMessage(message)) { - this.logger.trace('info message', message) - return next() - } - - if (!isAtLeastDeliveredOnce && this.config.logWarnOnMessagesWithoutReceiver) { - const err = new UnhandledError( - StatusCode.BadGateway, - 'InvalidMessage: received a message which is not consumed by any service command or subscription', - message, - ) - this.logger.warn({ err, ...span.spanContext(), customTraceId: message.traceId }, err.message) - this.emit(EventBridgeEventNames.EventbridgeError, err) - } - - return next() - } catch (error) { - const err = new UnhandledError(StatusCode.InternalServerError, 'eventbus failure', error) - this.emit(EventBridgeEventNames.EventbridgeError, err) - this.logger.error({ err, ...span.spanContext() }, err.message) - - span.recordException(err) - - span.setStatus({ - code: SpanStatusCode.ERROR, - message: err.message, - }) - - this.healthy = false - - return next(error as Error) - } - }, - ) - } - - this.writeStream._write = write.bind(this) - - this.readStream.pipe(this.writeStream) - - this.emit(EventBridgeEventNames.EventbridgeConnected) - - this.logger.info({ puristaVersion }, 'DefaultEventBridge started') - - this.hasStarted = true - this.healthy = true - } - - /** - * Register a service command and ensure that there is a queue for all incoming command requests. - * @param address The service function address - * @param cb the function to call if a matching command message arrives - * @returns the id of command function queue - */ - async registerCommand( - address: EBMessageAddress, - cb: (message: Command) => Promise | CommandErrorResponse>, - metadata: CommandDefinitionMetadataBase, - ): Promise { - const queueName = getCommandQueueName(address) - this.serviceFunctions.set(queueName, cb) - - const info = createInfoMessage( - EBMessageType.InfoServiceFunctionAdded, - { ...address, instanceId: this.instanceId }, - { - payload: metadata, - }, - ) - await this.emitMessage(info) - - return queueName - } - - async unregisterCommand(address: EBMessageAddress): Promise { - const queueName = getCommandQueueName(address) - this.serviceFunctions.delete(queueName) - } - - async registerSubscription( - subscription: Subscription, - cb: (message: EBMessage) => Promise | undefined>, - ): Promise { - const queueName = getSubscriptionQueueName(subscription.subscriber) - const entry = getNewSubscriptionStorageEntry(subscription, cb) - this.subscriptions.set(queueName, entry) - return queueName - } - - async unregisterSubscription(address: EBMessageAddress): Promise { - const queueName = getSubscriptionQueueName(address) - this.subscriptions.delete(queueName) - } - - /** - * Emit a new message to event bridge to be delivered to receiver - * - * @param message EBMessage - */ - async emitMessage(message: Omit): Promise> { - const context = deserializeOtp(this.logger, message.otp) - - const name = isCommandResponse(message as EBMessage) - ? PuristaSpanName.EventBridgeCommandResponse - : PuristaSpanName.EventBridgeEmitMessage - - return this.startActiveSpan(name, { kind: SpanKind.PRODUCER }, context, async (span) => { - try { - const msg = Object.freeze({ - ...message, - id: getNewEBMessageId(), - timestamp: Date.now(), - traceId: message.traceId, - instanceId: this.instanceId, - otp: serializeOtp(), - }) - - span.setAttribute(PuristaSpanTag.SenderServiceName, msg.sender.serviceName) - span.setAttribute(PuristaSpanTag.SenderServiceVersion, msg.sender.serviceVersion) - span.setAttribute(PuristaSpanTag.SenderServiceTarget, msg.sender.serviceTarget) - - this.readStream.push(msg) - - if (this.config.emitMessagesAsEventBridgeEvents && msg.eventName) { - this.emit(`custom-${msg.eventName}`, msg) - } - - return msg as Readonly - } catch (err) { - span.recordException(err as Error) - span.setStatus({ - code: SpanStatusCode.ERROR, - message: (err as Error).message, - }) - this.logger.error({ err, ...span.spanContext(), customTraceId: message.traceId }, 'emitMessage failed') - throw err - } - }) - } - - async invoke( - input: Omit, - commandTimeout = this.defaultCommandTimeout, - ): Promise { - const context = deserializeOtp(this.logger, input.otp) - - return this.startActiveSpan(PuristaSpanName.EventBridgeInvokeCommand, {}, context, async (_span) => { - const correlationId = getNewCorrelationId() - - const command: Command = Object.freeze({ - ...input, - otp: serializeOtp(), - sender: { - ...input.sender, - instanceId: this.instanceId, - }, - id: getNewEBMessageId(), - correlationId: getNewCorrelationId(), - timestamp: Date.now(), - messageType: EBMessageType.Command, - traceId: input.traceId, - }) - - const removeFromPending = () => { - this.pendingInvocations.delete(correlationId) - } - - const executionPromise = new Promise((resolve, reject) => { - const timeout = setTimeout(() => { - const err = new UnhandledError(StatusCode.GatewayTimeout, 'invocation timed out', undefined, command.traceId) - this.logger.warn({ err }) - rejectFn(err) - }, commandTimeout) - - const resolveFn = (successPayload: T) => { - clearTimeout(timeout) - removeFromPending() - resolve(successPayload) - } - - const rejectFn = (err: unknown) => { - clearTimeout(timeout) - removeFromPending() - reject(err) - } - - this.pendingInvocations.set(command.correlationId, { - resolve: resolveFn, - reject: rejectFn, - }) - }) - - this.emitMessage(command) - return executionPromise - }) - } - - async destroy(): Promise { - await super.destroy() - - let isTimedOut = false - const timeout = setTimeout(() => { - isTimedOut = true - }, this.defaultCommandTimeout) - - // ensure actual running commands and subscriptions are finished before closing connection - const waitForExecutionEnd = () => { - if (this.pendingInvocations.size <= 0 && this.runningSubscriptionCount <= 0) { - return - } - if (isTimedOut) { - this.logger.error('Some commands or subscriptions could not finish before connection was closed') - return - } - setImmediate(waitForExecutionEnd) - } - - waitForExecutionEnd() - if (timeout) { - clearTimeout(timeout) - } - - this.emit(EventBridgeEventNames.EventbridgeDisconnected) - - this.pendingInvocations.forEach((value) => value.reject(new UnhandledError(StatusCode.ServiceUnavailable))) - this.pendingInvocations.clear() - this.removeAllListeners() - this.writeStream.end().removeAllListeners() - this.readStream.destroy() - this.hasStarted = false - this.healthy = false - } + protected writeStream = new Stream.Writable({ objectMode: true }) + protected readStream = new Stream.Readable({ + objectMode: true, + read() { + /* nothing to do here */ + }, + }) + + protected serviceFunctions = new Map< + string, + (message: Command) => Promise + >() + + protected pendingInvocations = new Map() + protected runningSubscriptionCount = 0 + + protected subscriptions = new Map() + + protected hasStarted = false + protected healthy = false + + constructor(config?: EventBridgeConfig) { + const conf = { + ...getDefaultEventBridgeConfig(), + logWarnOnMessagesWithoutReceiver: true, + ...config, + } + super('DefaultEventBridge', conf) + } + + async isReady() { + return this.hasStarted + } + + async isHealthy() { + return this.hasStarted + } + + async start() { + await super.start() + const write = async (message: Readonly, _encoding: string, next: (error?: Error) => void) => { + const context = deserializeOtp(this.logger, message.otp) + + return this.startActiveSpan( + PuristaSpanName.EventBridgeHandleIncomingMessage, + { kind: SpanKind.CONSUMER }, + context, + async span => { + try { + let isAtLeastDeliveredOnce = false + for (const [_, subscription] of this.subscriptions) { + if (isMessageMatchingSubscription(this.logger, message, subscription)) { + isAtLeastDeliveredOnce = true + this.runningSubscriptionCount++ + subscription + .cb(message) + .then(result => { + if (subscription.emitEventName && result) { + this.emitMessage(result) + } + }) + .catch(err => this.logger.error({ err })) + .finally(() => this.runningSubscriptionCount--) + } + } + + if (isCommand(message)) { + const mapEntry = this.serviceFunctions.get(getCommandQueueName(message.receiver)) + if (!mapEntry) { + const err = new UnhandledError( + StatusCode.BadGateway, + 'InvalidCommand: received invalid command', + getCleanedMessage(message), + ) + span.setStatus({ + code: SpanStatusCode.ERROR, + message: err.message, + }) + span.recordException(err) + this.logger.error({ err, ...span.spanContext(), customTraceId: message.traceId }, err.message) + this.emit(EventBridgeEventNames.EventbridgeError, err) + + const errorResponse = createErrorResponse(this.instanceId, message, StatusCode.BadGateway, err) + this.emitMessage(errorResponse) + return next() + } + + isAtLeastDeliveredOnce = true + mapEntry(message as Readonly).then(result => { + this.emitMessage(result) + }) + return next() + } + + if (isCommandResponse(message)) { + const mapEntry = this.pendingInvocations.get(message.correlationId) + if (!mapEntry) { + const err = new UnhandledError( + StatusCode.BadGateway, + 'InvalidCommandResponse: received invalid command response', + getCleanedMessage(message), + ) + span.setStatus({ + code: SpanStatusCode.ERROR, + message: err.message, + }) + span.recordException(err) + this.logger.error({ err, ...span.spanContext(), customTraceId: message.traceId }, err.message) + this.emit(EventBridgeEventNames.EventbridgeError, err) + return next() + } + + isAtLeastDeliveredOnce = true + if (isCommandSuccessResponse(message)) { + mapEntry.resolve(message.payload) + } else if (isCommandErrorResponse(message)) { + const error = message.isHandledError + ? HandledError.fromMessage(message) + : UnhandledError.fromMessage(message) + mapEntry.reject(error) + } + return next() + } + + if (isInfoMessage(message)) { + this.logger.trace('info message', message) + return next() + } + + if (!isAtLeastDeliveredOnce && this.config.logWarnOnMessagesWithoutReceiver) { + const err = new UnhandledError( + StatusCode.BadGateway, + 'InvalidMessage: received a message which is not consumed by any service command or subscription', + message, + ) + this.logger.warn({ err, ...span.spanContext(), customTraceId: message.traceId }, err.message) + this.emit(EventBridgeEventNames.EventbridgeError, err) + } + + return next() + } catch (error) { + const err = new UnhandledError(StatusCode.InternalServerError, 'eventbus failure', error) + this.emit(EventBridgeEventNames.EventbridgeError, err) + this.logger.error({ err, ...span.spanContext() }, err.message) + + span.recordException(err) + + span.setStatus({ + code: SpanStatusCode.ERROR, + message: err.message, + }) + + this.healthy = false + + return next(error as Error) + } + }, + ) + } + + this.writeStream._write = write.bind(this) + + this.readStream.pipe(this.writeStream) + + this.emit(EventBridgeEventNames.EventbridgeConnected) + + this.logger.info({ puristaVersion }, 'DefaultEventBridge started') + + this.hasStarted = true + this.healthy = true + } + + /** + * Register a service command and ensure that there is a queue for all incoming command requests. + * @param address The service function address + * @param cb the function to call if a matching command message arrives + * @returns the id of command function queue + */ + async registerCommand( + address: EBMessageAddress, + cb: (message: Command) => Promise | CommandErrorResponse>, + metadata: CommandDefinitionMetadataBase, + ): Promise { + const queueName = getCommandQueueName(address) + this.serviceFunctions.set(queueName, cb) + + const info = createInfoMessage( + EBMessageType.InfoServiceFunctionAdded, + { ...address, instanceId: this.instanceId }, + { + payload: metadata, + }, + ) + await this.emitMessage(info) + + return queueName + } + + async unregisterCommand(address: EBMessageAddress): Promise { + const queueName = getCommandQueueName(address) + this.serviceFunctions.delete(queueName) + } + + async registerSubscription( + subscription: Subscription, + cb: (message: EBMessage) => Promise | undefined>, + ): Promise { + const queueName = getSubscriptionQueueName(subscription.subscriber) + const entry = getNewSubscriptionStorageEntry(subscription, cb) + this.subscriptions.set(queueName, entry) + return queueName + } + + async unregisterSubscription(address: EBMessageAddress): Promise { + const queueName = getSubscriptionQueueName(address) + this.subscriptions.delete(queueName) + } + + /** + * Emit a new message to event bridge to be delivered to receiver + * + * @param message EBMessage + */ + async emitMessage(message: Omit): Promise> { + const context = deserializeOtp(this.logger, message.otp) + + const name = isCommandResponse(message as EBMessage) + ? PuristaSpanName.EventBridgeCommandResponse + : PuristaSpanName.EventBridgeEmitMessage + + return this.startActiveSpan(name, { kind: SpanKind.PRODUCER }, context, async span => { + try { + const msg = Object.freeze({ + ...message, + id: getNewEBMessageId(), + timestamp: Date.now(), + traceId: message.traceId, + instanceId: this.instanceId, + otp: serializeOtp(), + }) + + span.setAttribute(PuristaSpanTag.SenderServiceName, msg.sender.serviceName) + span.setAttribute(PuristaSpanTag.SenderServiceVersion, msg.sender.serviceVersion) + span.setAttribute(PuristaSpanTag.SenderServiceTarget, msg.sender.serviceTarget) + + this.readStream.push(msg) + + if (this.config.emitMessagesAsEventBridgeEvents && msg.eventName) { + this.emit(`custom-${msg.eventName}`, msg) + } + + return msg as Readonly + } catch (err) { + span.recordException(err as Error) + span.setStatus({ + code: SpanStatusCode.ERROR, + message: (err as Error).message, + }) + this.logger.error({ err, ...span.spanContext(), customTraceId: message.traceId }, 'emitMessage failed') + throw err + } + }) + } + + async invoke( + input: Omit, + commandTimeout = this.defaultCommandTimeout, + ): Promise { + const context = deserializeOtp(this.logger, input.otp) + + return this.startActiveSpan(PuristaSpanName.EventBridgeInvokeCommand, {}, context, async _span => { + const correlationId = getNewCorrelationId() + + const command: Command = Object.freeze({ + ...input, + otp: serializeOtp(), + sender: { + ...input.sender, + instanceId: this.instanceId, + }, + id: getNewEBMessageId(), + correlationId: getNewCorrelationId(), + timestamp: Date.now(), + messageType: EBMessageType.Command, + traceId: input.traceId, + }) + + const removeFromPending = () => { + this.pendingInvocations.delete(correlationId) + } + + const executionPromise = new Promise((resolve, reject) => { + const timeout = setTimeout(() => { + const err = new UnhandledError(StatusCode.GatewayTimeout, 'invocation timed out', undefined, command.traceId) + this.logger.warn({ err }) + rejectFn(err) + }, commandTimeout) + + const resolveFn = (successPayload: T) => { + clearTimeout(timeout) + removeFromPending() + resolve(successPayload) + } + + const rejectFn = (err: unknown) => { + clearTimeout(timeout) + removeFromPending() + reject(err) + } + + this.pendingInvocations.set(command.correlationId, { + resolve: resolveFn, + reject: rejectFn, + }) + }) + + this.emitMessage(command) + return executionPromise + }) + } + + async destroy(): Promise { + await super.destroy() + + let isTimedOut = false + const timeout = setTimeout(() => { + isTimedOut = true + }, this.defaultCommandTimeout) + + // ensure actual running commands and subscriptions are finished before closing connection + const waitForExecutionEnd = () => { + if (this.pendingInvocations.size <= 0 && this.runningSubscriptionCount <= 0) { + return + } + if (isTimedOut) { + this.logger.error('Some commands or subscriptions could not finish before connection was closed') + return + } + setImmediate(waitForExecutionEnd) + } + + waitForExecutionEnd() + if (timeout) { + clearTimeout(timeout) + } + + this.emit(EventBridgeEventNames.EventbridgeDisconnected) + + for (const [_, value] of this.pendingInvocations) { + value.reject(new UnhandledError(StatusCode.ServiceUnavailable)) + } + this.pendingInvocations.clear() + this.removeAllListeners() + this.writeStream.end().removeAllListeners() + this.readStream.destroy() + this.hasStarted = false + this.healthy = false + } } diff --git a/packages/core/src/DefaultEventBridge/defaultEventBridge.test.ts b/packages/core/src/DefaultEventBridge/defaultEventBridge.test.ts index a5d1df282..7629c46af 100644 --- a/packages/core/src/DefaultEventBridge/defaultEventBridge.test.ts +++ b/packages/core/src/DefaultEventBridge/defaultEventBridge.test.ts @@ -1,162 +1,162 @@ import { assert, spy, stub } from 'sinon' import type { Subscription } from '../core/index.js' -import { createInfoMessage, EBMessageType } from '../core/index.js' +import { EBMessageType, createInfoMessage } from '../core/index.js' import { getCustomMessageMessageMock, getLoggerMock } from '../mocks/index.js' import { DefaultEventBridge } from './DefaultEventBridge.impl.js' describe('DefaultEventBridge', () => { - const sender = { - serviceName: 'SenderService', - serviceVersion: '1', - serviceTarget: 'senderServiceTarget', - instanceId: 'a', - } - - const receiver = { - serviceName: 'ReceiverService', - serviceVersion: '2', - serviceTarget: 'receiverServiceTarget', - } - - const subscriber = { - serviceName: 'SubscriberService', - serviceVersion: '3', - serviceTarget: 'subscriberServiceTarget', - } - - const otherSubscriber = { - serviceName: 'OtherSubscriberService', - serviceVersion: '4', - serviceTarget: 'otherSubscriberServiceTarget', - } - - const eventName = 'testEventName' - - it('creates a DefaultEventBridge', () => { - const logger = getLoggerMock() - - const eventBridge = new DefaultEventBridge({ logger: logger.mock }) - - expect(eventBridge).toBeDefined() - }) - - it('routes custom messages to subscriptions', async () => { - const logger = getLoggerMock() - - const eventBridge = new DefaultEventBridge({ logger: logger.mock }) - await eventBridge.start() - - const callback = stub().resolves() - - const subscription: Subscription = { - sender, - subscriber, - eventBridgeConfig: { - autoacknowledge: true, - shared: true, - durable: false, - }, - } - - const otherCall = stub().resolves() - const otherSubscription: Subscription = { - sender: { - serviceName: 'SomeService', - }, - subscriber: otherSubscriber, - eventBridgeConfig: { - durable: false, - autoacknowledge: true, - shared: true, - }, - } - - eventBridge.registerSubscription(subscription, callback) - eventBridge.registerSubscription(otherSubscription, otherCall) - - const message = getCustomMessageMessageMock( - eventName, - { - parameter: { parameter: 1 }, - payload: { payload: 'content' }, - }, - { - sender, - receiver, - }, - ) - - const emittedMessage = await eventBridge.emitMessage(message) - await new Promise((resolve) => process.nextTick(resolve)) - - expect(callback.called).toBeTruthy() - expect(callback.callCount).toBe(1) - assert.calledWith(callback, emittedMessage) - - expect(otherCall.callCount).toBe(0) - - const unsubscribe = spy(eventBridge, 'unregisterSubscription') - - await eventBridge.unregisterSubscription(subscriber) - - expect(unsubscribe.callCount).toBe(1) - - callback.resetHistory() - logger.stubs.trace.resetHistory() - - await eventBridge.emitMessage(message) - await new Promise((resolve) => process.nextTick(resolve)) - - expect(callback.called).toBeFalsy() - - expect(logger.stubs.warn.getCall(0).args[1]).toBe( - 'InvalidMessage: received a message which is not consumed by any service command or subscription', - ) - expect(logger.stubs.error.called).toBeFalsy() - }) - - it('returns error if command is not found', async () => { - expect(true).toBeTruthy() - }) - - it('returns command success message', async () => { - expect(true).toBeTruthy() - }) - - it('returns command error message', async () => { - expect(true).toBeTruthy() - }) - - it('traces info messages', async () => { - const logger = getLoggerMock() - const eventBridge = new DefaultEventBridge({ logger: logger.mock }) - await eventBridge.start() + const sender = { + serviceName: 'SenderService', + serviceVersion: '1', + serviceTarget: 'senderServiceTarget', + instanceId: 'a', + } + + const receiver = { + serviceName: 'ReceiverService', + serviceVersion: '2', + serviceTarget: 'receiverServiceTarget', + } + + const subscriber = { + serviceName: 'SubscriberService', + serviceVersion: '3', + serviceTarget: 'subscriberServiceTarget', + } + + const otherSubscriber = { + serviceName: 'OtherSubscriberService', + serviceVersion: '4', + serviceTarget: 'otherSubscriberServiceTarget', + } + + const eventName = 'testEventName' + + it('creates a DefaultEventBridge', () => { + const logger = getLoggerMock() + + const eventBridge = new DefaultEventBridge({ logger: logger.mock }) + + expect(eventBridge).toBeDefined() + }) + + it('routes custom messages to subscriptions', async () => { + const logger = getLoggerMock() + + const eventBridge = new DefaultEventBridge({ logger: logger.mock }) + await eventBridge.start() + + const callback = stub().resolves() + + const subscription: Subscription = { + sender, + subscriber, + eventBridgeConfig: { + autoacknowledge: true, + shared: true, + durable: false, + }, + } + + const otherCall = stub().resolves() + const otherSubscription: Subscription = { + sender: { + serviceName: 'SomeService', + }, + subscriber: otherSubscriber, + eventBridgeConfig: { + durable: false, + autoacknowledge: true, + shared: true, + }, + } + + eventBridge.registerSubscription(subscription, callback) + eventBridge.registerSubscription(otherSubscription, otherCall) + + const message = getCustomMessageMessageMock( + eventName, + { + parameter: { parameter: 1 }, + payload: { payload: 'content' }, + }, + { + sender, + receiver, + }, + ) + + const emittedMessage = await eventBridge.emitMessage(message) + await new Promise(resolve => process.nextTick(resolve)) + + expect(callback.called).toBeTruthy() + expect(callback.callCount).toBe(1) + assert.calledWith(callback, emittedMessage) + + expect(otherCall.callCount).toBe(0) + + const unsubscribe = spy(eventBridge, 'unregisterSubscription') + + await eventBridge.unregisterSubscription(subscriber) + + expect(unsubscribe.callCount).toBe(1) + + callback.resetHistory() + logger.stubs.trace.resetHistory() + + await eventBridge.emitMessage(message) + await new Promise(resolve => process.nextTick(resolve)) + + expect(callback.called).toBeFalsy() + + expect(logger.stubs.warn.getCall(0).args[1]).toBe( + 'InvalidMessage: received a message which is not consumed by any service command or subscription', + ) + expect(logger.stubs.error.called).toBeFalsy() + }) + + it('returns error if command is not found', async () => { + expect(true).toBeTruthy() + }) + + it('returns command success message', async () => { + expect(true).toBeTruthy() + }) + + it('returns command error message', async () => { + expect(true).toBeTruthy() + }) + + it('traces info messages', async () => { + const logger = getLoggerMock() + const eventBridge = new DefaultEventBridge({ logger: logger.mock }) + await eventBridge.start() - const callback = stub().resolves() - - const subscription: Subscription = { - sender, - subscriber, - eventBridgeConfig: { - durable: false, - autoacknowledge: true, - shared: true, - }, - } - - eventBridge.registerSubscription(subscription, callback) - - const message = createInfoMessage(EBMessageType.InfoServiceFunctionAdded, sender, { - payload: { some: 'data' }, - }) - - const emittedMessage = await eventBridge.emitMessage(message) - await new Promise((resolve) => process.nextTick(resolve)) - - expect(callback.callCount).toBe(1) - assert.calledWith(callback, emittedMessage) - - expect(logger.stubs.trace.called).toBeTruthy() - }) + const callback = stub().resolves() + + const subscription: Subscription = { + sender, + subscriber, + eventBridgeConfig: { + durable: false, + autoacknowledge: true, + shared: true, + }, + } + + eventBridge.registerSubscription(subscription, callback) + + const message = createInfoMessage(EBMessageType.InfoServiceFunctionAdded, sender, { + payload: { some: 'data' }, + }) + + const emittedMessage = await eventBridge.emitMessage(message) + await new Promise(resolve => process.nextTick(resolve)) + + expect(callback.callCount).toBe(1) + assert.calledWith(callback, emittedMessage) + + expect(logger.stubs.trace.called).toBeTruthy() + }) }) diff --git a/packages/core/src/DefaultEventBridge/getDefaultEventBridgeConfig.impl.ts b/packages/core/src/DefaultEventBridge/getDefaultEventBridgeConfig.impl.ts index 0c9140205..bd270bffb 100644 --- a/packages/core/src/DefaultEventBridge/getDefaultEventBridgeConfig.impl.ts +++ b/packages/core/src/DefaultEventBridge/getDefaultEventBridgeConfig.impl.ts @@ -2,10 +2,10 @@ import type { Complete } from '../core/index.js' import type { DefaultEventBridgeConfig } from './types/index.js' export const getDefaultEventBridgeConfig = (): Complete => { - const defaultConfig: Complete = { - logWarnOnMessagesWithoutReceiver: true, - emitMessagesAsEventBridgeEvents: false, - } + const defaultConfig: Complete = { + logWarnOnMessagesWithoutReceiver: true, + emitMessagesAsEventBridgeEvents: false, + } - return defaultConfig + return defaultConfig } diff --git a/packages/core/src/DefaultEventBridge/getDefaultEventBridgeConfig.test.ts b/packages/core/src/DefaultEventBridge/getDefaultEventBridgeConfig.test.ts index 47a4d9b11..e44bf3e65 100644 --- a/packages/core/src/DefaultEventBridge/getDefaultEventBridgeConfig.test.ts +++ b/packages/core/src/DefaultEventBridge/getDefaultEventBridgeConfig.test.ts @@ -1,8 +1,8 @@ import { getDefaultEventBridgeConfig } from './getDefaultEventBridgeConfig.impl.js' it('returns default config for DefaultEventBridge', () => { - const config = getDefaultEventBridgeConfig() + const config = getDefaultEventBridgeConfig() - expect(config).toBeDefined() - expect(config.logWarnOnMessagesWithoutReceiver).toBeTruthy() + expect(config).toBeDefined() + expect(config.logWarnOnMessagesWithoutReceiver).toBeTruthy() }) diff --git a/packages/core/src/DefaultEventBridge/getNewSubscriptionStorageEntry.impl.ts b/packages/core/src/DefaultEventBridge/getNewSubscriptionStorageEntry.impl.ts index 483c391f2..4cff9bcac 100644 --- a/packages/core/src/DefaultEventBridge/getNewSubscriptionStorageEntry.impl.ts +++ b/packages/core/src/DefaultEventBridge/getNewSubscriptionStorageEntry.impl.ts @@ -2,73 +2,73 @@ import type { CustomMessage, EBMessage, EBMessageType, Subscription } from '../c import type { SubscriptionStorageEntry } from './types/index.js' export const getNewSubscriptionStorageEntry = ( - subscription: Subscription, - cb: (message: EBMessage) => Promise | undefined>, + subscription: Subscription, + cb: (message: EBMessage) => Promise | undefined>, ): SubscriptionStorageEntry => { - const entry: SubscriptionStorageEntry = { - isMatchingMessageType: () => true, - isMatchingSenderServiceName: () => true, - isMatchingSenderServiceVersion: () => true, - isMatchingSenderServiceTarget: () => true, - isMatchingSenderInstanceId: () => true, - isMatchingReceiverServiceName: () => true, - isMatchingReceiverServiceVersion: () => true, - isMatchingReceiverServiceTarget: () => true, - isMatchingReceiverInstanceId: () => true, - isMatchingPrincipalId: () => true, - isMatchingTenantId: () => true, - isMatchingEventName: () => true, - emitEventName: subscription.emitEventName, - cb, - } + const entry: SubscriptionStorageEntry = { + isMatchingMessageType: () => true, + isMatchingSenderServiceName: () => true, + isMatchingSenderServiceVersion: () => true, + isMatchingSenderServiceTarget: () => true, + isMatchingSenderInstanceId: () => true, + isMatchingReceiverServiceName: () => true, + isMatchingReceiverServiceVersion: () => true, + isMatchingReceiverServiceTarget: () => true, + isMatchingReceiverInstanceId: () => true, + isMatchingPrincipalId: () => true, + isMatchingTenantId: () => true, + isMatchingEventName: () => true, + emitEventName: subscription.emitEventName, + cb, + } - if (subscription.principalId) { - entry.isMatchingPrincipalId = (input: EBMessageType) => input === subscription.principalId - } + if (subscription.principalId) { + entry.isMatchingPrincipalId = (input: EBMessageType) => input === subscription.principalId + } - if (subscription.tenantId) { - entry.isMatchingTenantId = (input: EBMessageType) => input === subscription.tenantId - } + if (subscription.tenantId) { + entry.isMatchingTenantId = (input: EBMessageType) => input === subscription.tenantId + } - if (subscription.sender?.instanceId) { - entry.isMatchingSenderInstanceId = (input: EBMessageType) => input === subscription.sender?.instanceId - } + if (subscription.sender?.instanceId) { + entry.isMatchingSenderInstanceId = (input: EBMessageType) => input === subscription.sender?.instanceId + } - if (subscription.receiver?.instanceId) { - entry.isMatchingReceiverInstanceId = (input: EBMessageType) => input === subscription.receiver?.instanceId - } + if (subscription.receiver?.instanceId) { + entry.isMatchingReceiverInstanceId = (input: EBMessageType) => input === subscription.receiver?.instanceId + } - if (subscription.messageType) { - entry.isMatchingMessageType = (input: EBMessageType) => input === subscription.messageType - } + if (subscription.messageType) { + entry.isMatchingMessageType = (input: EBMessageType) => input === subscription.messageType + } - if (subscription.eventName) { - entry.isMatchingEventName = (input?: string) => input === subscription.eventName - } + if (subscription.eventName) { + entry.isMatchingEventName = (input?: string) => input === subscription.eventName + } - if (subscription.sender?.serviceName) { - entry.isMatchingSenderServiceName = (input?: string) => input === subscription.sender?.serviceName - } + if (subscription.sender?.serviceName) { + entry.isMatchingSenderServiceName = (input?: string) => input === subscription.sender?.serviceName + } - if (subscription.sender?.serviceVersion) { - entry.isMatchingSenderServiceVersion = (input?: string) => input === subscription.sender?.serviceVersion - } + if (subscription.sender?.serviceVersion) { + entry.isMatchingSenderServiceVersion = (input?: string) => input === subscription.sender?.serviceVersion + } - if (subscription.sender?.serviceTarget) { - entry.isMatchingSenderServiceTarget = (input?: string) => input === subscription.sender?.serviceTarget - } + if (subscription.sender?.serviceTarget) { + entry.isMatchingSenderServiceTarget = (input?: string) => input === subscription.sender?.serviceTarget + } - if (subscription.receiver?.serviceName) { - entry.isMatchingReceiverServiceName = (input?: string) => input === subscription.receiver?.serviceName - } + if (subscription.receiver?.serviceName) { + entry.isMatchingReceiverServiceName = (input?: string) => input === subscription.receiver?.serviceName + } - if (subscription.receiver?.serviceVersion) { - entry.isMatchingReceiverServiceVersion = (input?: string) => input === subscription.receiver?.serviceVersion - } + if (subscription.receiver?.serviceVersion) { + entry.isMatchingReceiverServiceVersion = (input?: string) => input === subscription.receiver?.serviceVersion + } - if (subscription.receiver?.serviceTarget) { - entry.isMatchingReceiverServiceTarget = (input?: string) => input === subscription.receiver?.serviceTarget - } + if (subscription.receiver?.serviceTarget) { + entry.isMatchingReceiverServiceTarget = (input?: string) => input === subscription.receiver?.serviceTarget + } - return entry + return entry } diff --git a/packages/core/src/DefaultEventBridge/isMessageMatchingSubscription.impl.ts b/packages/core/src/DefaultEventBridge/isMessageMatchingSubscription.impl.ts index 508f1b2f2..a74f5ea87 100644 --- a/packages/core/src/DefaultEventBridge/isMessageMatchingSubscription.impl.ts +++ b/packages/core/src/DefaultEventBridge/isMessageMatchingSubscription.impl.ts @@ -3,79 +3,79 @@ import { isCommand, isCommandResponse, isCustomMessage, isInfoMessage } from '.. import type { SubscriptionStorageEntry } from './types/index.js' export const isMessageMatchingSubscription = ( - _log: Logger, - message: EBMessage, - subscription: SubscriptionStorageEntry, + _log: Logger, + message: EBMessage, + subscription: SubscriptionStorageEntry, ): boolean => { - // if message type does not match, the subscription does not match - if (!subscription.isMatchingMessageType(message.messageType)) { - return false - } + // if message type does not match, the subscription does not match + if (!subscription.isMatchingMessageType(message.messageType)) { + return false + } - // if message type does not match, the principal id does not match - if (!subscription.isMatchingPrincipalId(message.principalId)) { - return false - } + // if message type does not match, the principal id does not match + if (!subscription.isMatchingPrincipalId(message.principalId)) { + return false + } - // if message type does not match, the tenantId id does not match - if (!subscription.isMatchingTenantId(message.tenantId)) { - return false - } + // if message type does not match, the tenantId id does not match + if (!subscription.isMatchingTenantId(message.tenantId)) { + return false + } - // if we are looking for a named event, if is does not match, the subscription does not match - if (!subscription.isMatchingEventName(message.eventName)) { - return false - } + // if we are looking for a named event, if is does not match, the subscription does not match + if (!subscription.isMatchingEventName(message.eventName)) { + return false + } - if (isInfoMessage(message)) { - // info messages do not have receivers - if ( - !subscription.isMatchingReceiverServiceName() || - !subscription.isMatchingReceiverServiceVersion() || - !subscription.isMatchingReceiverServiceTarget() || - !subscription.isMatchingReceiverInstanceId() - ) { - return false - } + if (isInfoMessage(message)) { + // info messages do not have receivers + if ( + !subscription.isMatchingReceiverServiceName() || + !subscription.isMatchingReceiverServiceVersion() || + !subscription.isMatchingReceiverServiceTarget() || + !subscription.isMatchingReceiverInstanceId() + ) { + return false + } - // if sender does not match subscription - if ( - !subscription.isMatchingSenderServiceName(message.sender.serviceName) || - !subscription.isMatchingSenderServiceTarget(message.sender.serviceTarget) || - !subscription.isMatchingSenderServiceVersion(message.sender.serviceVersion) || - !subscription.isMatchingSenderInstanceId(message.sender.instanceId) - ) { - return false - } + // if sender does not match subscription + if ( + !subscription.isMatchingSenderServiceName(message.sender.serviceName) || + !subscription.isMatchingSenderServiceTarget(message.sender.serviceTarget) || + !subscription.isMatchingSenderServiceVersion(message.sender.serviceVersion) || + !subscription.isMatchingSenderInstanceId(message.sender.instanceId) + ) { + return false + } - // it is a info message and the sender is matching - return true - } + // it is a info message and the sender is matching + return true + } - if (isCommandResponse(message) || isCommand(message) || isCustomMessage(message)) { - // if subscription is looking for specific sender we check for match - if ( - !subscription.isMatchingSenderServiceName(message.sender.serviceName) || - !subscription.isMatchingSenderServiceTarget(message.sender.serviceTarget) || - !subscription.isMatchingSenderServiceVersion(message.sender.serviceVersion) || - !subscription.isMatchingSenderInstanceId(message.sender.instanceId) - ) { - return false - } + if (isCommandResponse(message) || isCommand(message) || isCustomMessage(message)) { + // if subscription is looking for specific sender we check for match + if ( + !subscription.isMatchingSenderServiceName(message.sender.serviceName) || + !subscription.isMatchingSenderServiceTarget(message.sender.serviceTarget) || + !subscription.isMatchingSenderServiceVersion(message.sender.serviceVersion) || + !subscription.isMatchingSenderInstanceId(message.sender.instanceId) + ) { + return false + } - if ( - !subscription.isMatchingReceiverServiceName(message.receiver?.serviceName) || - !subscription.isMatchingReceiverServiceTarget(message.receiver?.serviceTarget) || - !subscription.isMatchingReceiverServiceVersion(message.receiver?.serviceVersion) || - !subscription.isMatchingReceiverInstanceId(message.receiver?.instanceId) - ) { - return false - } + if ( + !subscription.isMatchingReceiverServiceName(message.receiver?.serviceName) || + !subscription.isMatchingReceiverServiceTarget(message.receiver?.serviceTarget) || + !subscription.isMatchingReceiverServiceVersion(message.receiver?.serviceVersion) || + !subscription.isMatchingReceiverInstanceId(message.receiver?.instanceId) + ) { + return false + } - return true - } + return true + } - // eg. maybe there are additional types in future - // so every message-type we do not handle explicit will not match - return false + // eg. maybe there are additional types in future + // so every message-type we do not handle explicit will not match + return false } diff --git a/packages/core/src/DefaultEventBridge/test/infoMessageMatching.test.ts b/packages/core/src/DefaultEventBridge/test/infoMessageMatching.test.ts index 5de942458..7af9f1866 100644 --- a/packages/core/src/DefaultEventBridge/test/infoMessageMatching.test.ts +++ b/packages/core/src/DefaultEventBridge/test/infoMessageMatching.test.ts @@ -7,222 +7,222 @@ import { getNewSubscriptionStorageEntry } from '../getNewSubscriptionStorageEntr import { isMessageMatchingSubscription } from '../isMessageMatchingSubscription.impl.js' describe('subscription matching for info message', () => { - const sender = { - serviceName: 'SenderService', - serviceVersion: '1', - serviceTarget: 'senderServiceTarget', - instanceId: 'instanceId', - } - - const receiver = { - serviceName: 'ReceiverService', - serviceVersion: '2', - serviceTarget: 'receiverServiceTarget', - instanceId: 'instanceId', - } - - const subscriber = { - serviceName: 'SubscriberService', - serviceVersion: '3', - serviceTarget: 'subscriberServiceTarget', - instanceId: 'instanceId', - } - - const callback = stub().resolves() - - const eventName = 'testEventName' - - const getTestMessage = (): InfoMessage => { - return { - sender, - messageType: EBMessageType.InfoServiceInit, - id: 'messageTestId', - traceId: 'messageTraceId', - timestamp: Date.now(), - correlationId: 'messageCorrelationId', - principalId: 'messagePrincipalId', - tenantId: 'messageTenantId', - eventName, - payload: {}, - contentType: 'application/json', - contentEncoding: 'utf-8', - } - } - - it('matches on sender service name', () => { - const subscription: Subscription = { - sender: { - serviceName: sender.serviceName, - }, - subscriber, - eventBridgeConfig: { - durable: false, - autoacknowledge: true, - shared: true, - }, - } - - const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) - - const result = isMessageMatchingSubscription(getLoggerMock().mock, getTestMessage(), storageEntry) - - expect(result).toBeTruthy() - }) - - it('fails on different sender service name', () => { - const subscription: Subscription = { - sender: { - serviceName: 'differentService', - }, - subscriber, - eventBridgeConfig: { - durable: false, - autoacknowledge: true, - shared: true, - }, - } - - const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) - - const result = isMessageMatchingSubscription(getLoggerMock().mock, getTestMessage(), storageEntry) - - expect(result).toBeFalsy() - }) - - it('matches on sender service version', () => { - const subscription: Subscription = { - sender: { - serviceVersion: sender.serviceVersion, - }, - subscriber, - eventBridgeConfig: { - durable: false, - autoacknowledge: true, - shared: true, - }, - } - - const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) - - const result = isMessageMatchingSubscription(getLoggerMock().mock, getTestMessage(), storageEntry) - - expect(result).toBeTruthy() - }) - - it('fails on different sender service version', () => { - const subscription: Subscription = { - sender: { - serviceVersion: '9', - }, - subscriber, - eventBridgeConfig: { - durable: false, - autoacknowledge: true, - shared: true, - }, - } - - const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) - - const result = isMessageMatchingSubscription(getLoggerMock().mock, getTestMessage(), storageEntry) - - expect(result).toBeFalsy() - }) - - it('matches on sender service target', () => { - const subscription: Subscription = { - sender: { - serviceTarget: sender.serviceTarget, - }, - subscriber, - eventBridgeConfig: { - durable: false, - autoacknowledge: true, - shared: true, - }, - } - - const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) - - const result = isMessageMatchingSubscription(getLoggerMock().mock, getTestMessage(), storageEntry) - - expect(result).toBeTruthy() - }) - - it('fails on different sender service target', () => { - const subscription: Subscription = { - sender: { - serviceTarget: 'differentTarget', - }, - subscriber, - eventBridgeConfig: { - durable: false, - autoacknowledge: true, - shared: true, - }, - } - - const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) - - const result = isMessageMatchingSubscription(getLoggerMock().mock, getTestMessage(), storageEntry) - - expect(result).toBeFalsy() - }) - - it('matches on message type', () => { - const subscription: Subscription = { - messageType: EBMessageType.InfoServiceInit, - subscriber, - eventBridgeConfig: { - durable: false, - autoacknowledge: true, - shared: true, - }, - } - - const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) - - const result = isMessageMatchingSubscription(getLoggerMock().mock, getTestMessage(), storageEntry) - - expect(result).toBeTruthy() - }) - - it('fails on different message type', () => { - const subscription: Subscription = { - messageType: EBMessageType.InfoServiceDrain, - subscriber, - eventBridgeConfig: { - durable: false, - autoacknowledge: true, - shared: true, - }, - } - - const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) - - const result = isMessageMatchingSubscription(getLoggerMock().mock, getTestMessage(), storageEntry) - - expect(result).toBeFalsy() - }) - - it('fails if receiver is set in subscription', () => { - const subscription: Subscription = { - sender: { - serviceName: sender.serviceName, - }, - subscriber, - receiver, - eventBridgeConfig: { - durable: false, - autoacknowledge: true, - shared: true, - }, - } - - const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) - - const result = isMessageMatchingSubscription(getLoggerMock().mock, getTestMessage(), storageEntry) - - expect(result).toBeFalsy() - }) + const sender = { + serviceName: 'SenderService', + serviceVersion: '1', + serviceTarget: 'senderServiceTarget', + instanceId: 'instanceId', + } + + const receiver = { + serviceName: 'ReceiverService', + serviceVersion: '2', + serviceTarget: 'receiverServiceTarget', + instanceId: 'instanceId', + } + + const subscriber = { + serviceName: 'SubscriberService', + serviceVersion: '3', + serviceTarget: 'subscriberServiceTarget', + instanceId: 'instanceId', + } + + const callback = stub().resolves() + + const eventName = 'testEventName' + + const getTestMessage = (): InfoMessage => { + return { + sender, + messageType: EBMessageType.InfoServiceInit, + id: 'messageTestId', + traceId: 'messageTraceId', + timestamp: Date.now(), + correlationId: 'messageCorrelationId', + principalId: 'messagePrincipalId', + tenantId: 'messageTenantId', + eventName, + payload: {}, + contentType: 'application/json', + contentEncoding: 'utf-8', + } + } + + it('matches on sender service name', () => { + const subscription: Subscription = { + sender: { + serviceName: sender.serviceName, + }, + subscriber, + eventBridgeConfig: { + durable: false, + autoacknowledge: true, + shared: true, + }, + } + + const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) + + const result = isMessageMatchingSubscription(getLoggerMock().mock, getTestMessage(), storageEntry) + + expect(result).toBeTruthy() + }) + + it('fails on different sender service name', () => { + const subscription: Subscription = { + sender: { + serviceName: 'differentService', + }, + subscriber, + eventBridgeConfig: { + durable: false, + autoacknowledge: true, + shared: true, + }, + } + + const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) + + const result = isMessageMatchingSubscription(getLoggerMock().mock, getTestMessage(), storageEntry) + + expect(result).toBeFalsy() + }) + + it('matches on sender service version', () => { + const subscription: Subscription = { + sender: { + serviceVersion: sender.serviceVersion, + }, + subscriber, + eventBridgeConfig: { + durable: false, + autoacknowledge: true, + shared: true, + }, + } + + const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) + + const result = isMessageMatchingSubscription(getLoggerMock().mock, getTestMessage(), storageEntry) + + expect(result).toBeTruthy() + }) + + it('fails on different sender service version', () => { + const subscription: Subscription = { + sender: { + serviceVersion: '9', + }, + subscriber, + eventBridgeConfig: { + durable: false, + autoacknowledge: true, + shared: true, + }, + } + + const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) + + const result = isMessageMatchingSubscription(getLoggerMock().mock, getTestMessage(), storageEntry) + + expect(result).toBeFalsy() + }) + + it('matches on sender service target', () => { + const subscription: Subscription = { + sender: { + serviceTarget: sender.serviceTarget, + }, + subscriber, + eventBridgeConfig: { + durable: false, + autoacknowledge: true, + shared: true, + }, + } + + const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) + + const result = isMessageMatchingSubscription(getLoggerMock().mock, getTestMessage(), storageEntry) + + expect(result).toBeTruthy() + }) + + it('fails on different sender service target', () => { + const subscription: Subscription = { + sender: { + serviceTarget: 'differentTarget', + }, + subscriber, + eventBridgeConfig: { + durable: false, + autoacknowledge: true, + shared: true, + }, + } + + const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) + + const result = isMessageMatchingSubscription(getLoggerMock().mock, getTestMessage(), storageEntry) + + expect(result).toBeFalsy() + }) + + it('matches on message type', () => { + const subscription: Subscription = { + messageType: EBMessageType.InfoServiceInit, + subscriber, + eventBridgeConfig: { + durable: false, + autoacknowledge: true, + shared: true, + }, + } + + const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) + + const result = isMessageMatchingSubscription(getLoggerMock().mock, getTestMessage(), storageEntry) + + expect(result).toBeTruthy() + }) + + it('fails on different message type', () => { + const subscription: Subscription = { + messageType: EBMessageType.InfoServiceDrain, + subscriber, + eventBridgeConfig: { + durable: false, + autoacknowledge: true, + shared: true, + }, + } + + const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) + + const result = isMessageMatchingSubscription(getLoggerMock().mock, getTestMessage(), storageEntry) + + expect(result).toBeFalsy() + }) + + it('fails if receiver is set in subscription', () => { + const subscription: Subscription = { + sender: { + serviceName: sender.serviceName, + }, + subscriber, + receiver, + eventBridgeConfig: { + durable: false, + autoacknowledge: true, + shared: true, + }, + } + + const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) + + const result = isMessageMatchingSubscription(getLoggerMock().mock, getTestMessage(), storageEntry) + + expect(result).toBeFalsy() + }) }) diff --git a/packages/core/src/DefaultEventBridge/test/singleMatchingEventName.test.ts b/packages/core/src/DefaultEventBridge/test/singleMatchingEventName.test.ts index 0942ada31..31321c5e4 100644 --- a/packages/core/src/DefaultEventBridge/test/singleMatchingEventName.test.ts +++ b/packages/core/src/DefaultEventBridge/test/singleMatchingEventName.test.ts @@ -7,82 +7,82 @@ import { getNewSubscriptionStorageEntry } from '../getNewSubscriptionStorageEntr import { isMessageMatchingSubscription } from '../isMessageMatchingSubscription.impl.js' describe('subscription matching for even name', () => { - const sender = { - serviceName: 'SenderService', - serviceVersion: '1', - serviceTarget: 'senderServiceTarget', - instanceId: 'SenderServiceInstance', - } + const sender = { + serviceName: 'SenderService', + serviceVersion: '1', + serviceTarget: 'senderServiceTarget', + instanceId: 'SenderServiceInstance', + } - const receiver = { - serviceName: 'ReceiverService', - serviceVersion: '2', - serviceTarget: 'receiverServiceTarget', - instanceId: 'ReceiverServiceInstance', - } + const receiver = { + serviceName: 'ReceiverService', + serviceVersion: '2', + serviceTarget: 'receiverServiceTarget', + instanceId: 'ReceiverServiceInstance', + } - const subscriber = { - serviceName: 'SubscriberService', - serviceVersion: '3', - serviceTarget: 'subscriberServiceTarget', - instanceId: 'instanceId', - } + const subscriber = { + serviceName: 'SubscriberService', + serviceVersion: '3', + serviceTarget: 'subscriberServiceTarget', + instanceId: 'instanceId', + } - const callback = stub().resolves() + const callback = stub().resolves() - const eventName = 'testEventName' + const eventName = 'testEventName' - const getTestMessage = (): EBMessage => { - return { - sender, - receiver, - payload: {}, - messageType: EBMessageType.CommandSuccessResponse, - id: 'messageTestId', - traceId: 'messageTraceId', - timestamp: Date.now(), - correlationId: 'messageCorrelationId', - principalId: 'messagePrincipalId', - tenantId: 'messageTenantId', - eventName, - contentType: 'application/json', - contentEncoding: 'utf-8', - } - } + const getTestMessage = (): EBMessage => { + return { + sender, + receiver, + payload: {}, + messageType: EBMessageType.CommandSuccessResponse, + id: 'messageTestId', + traceId: 'messageTraceId', + timestamp: Date.now(), + correlationId: 'messageCorrelationId', + principalId: 'messagePrincipalId', + tenantId: 'messageTenantId', + eventName, + contentType: 'application/json', + contentEncoding: 'utf-8', + } + } - it('matches on event name', () => { - const subscription: Subscription = { - eventName, - subscriber, - eventBridgeConfig: { - durable: false, - autoacknowledge: true, - shared: true, - }, - } + it('matches on event name', () => { + const subscription: Subscription = { + eventName, + subscriber, + eventBridgeConfig: { + durable: false, + autoacknowledge: true, + shared: true, + }, + } - const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) + const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) - const result = isMessageMatchingSubscription(getLoggerMock().mock, getTestMessage(), storageEntry) + const result = isMessageMatchingSubscription(getLoggerMock().mock, getTestMessage(), storageEntry) - expect(result).toBeTruthy() - }) + expect(result).toBeTruthy() + }) - it('fails on different event name', () => { - const subscription: Subscription = { - eventName: 'otherEventName', - subscriber, - eventBridgeConfig: { - durable: false, - autoacknowledge: true, - shared: true, - }, - } + it('fails on different event name', () => { + const subscription: Subscription = { + eventName: 'otherEventName', + subscriber, + eventBridgeConfig: { + durable: false, + autoacknowledge: true, + shared: true, + }, + } - const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) + const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) - const result = isMessageMatchingSubscription(getLoggerMock().mock, getTestMessage(), storageEntry) + const result = isMessageMatchingSubscription(getLoggerMock().mock, getTestMessage(), storageEntry) - expect(result).toBeFalsy() - }) + expect(result).toBeFalsy() + }) }) diff --git a/packages/core/src/DefaultEventBridge/test/singleMatchingInstanceId.test.ts b/packages/core/src/DefaultEventBridge/test/singleMatchingInstanceId.test.ts index cc96e7a1d..e2ffc0bdf 100644 --- a/packages/core/src/DefaultEventBridge/test/singleMatchingInstanceId.test.ts +++ b/packages/core/src/DefaultEventBridge/test/singleMatchingInstanceId.test.ts @@ -7,127 +7,127 @@ import { getNewSubscriptionStorageEntry } from '../getNewSubscriptionStorageEntr import { isMessageMatchingSubscription } from '../isMessageMatchingSubscription.impl.js' describe('subscription matching for instanceId', () => { - const sender = { - serviceName: 'SenderService', - serviceVersion: '1', - serviceTarget: 'senderServiceTarget', - instanceId: 'SenderServiceInstance', - } - - const receiver = { - serviceName: 'ReceiverService', - serviceVersion: '2', - serviceTarget: 'receiverServiceTarget', - instanceId: 'ReceiverServiceInstance', - } - - const subscriber = { - serviceName: 'SubscriberService', - serviceVersion: '3', - serviceTarget: 'subscriberServiceTarget', - } - - const callback = stub().resolves() - - const eventName = 'testEventName' - - const getTestMessage = (): EBMessage => { - return { - sender, - receiver, - payload: {}, - messageType: EBMessageType.CommandSuccessResponse, - id: 'messageTestId', - traceId: 'messageTraceId', - timestamp: Date.now(), - correlationId: 'messageCorrelationId', - principalId: 'messagePrincipalId', - tenantId: 'messageTenantId', - eventName, - contentType: 'application/json', - contentEncoding: 'utf-8', - } - } - - it('matches on sender instanceId', () => { - const subscription: Subscription = { - sender: { - instanceId: 'SenderServiceInstance', - }, - subscriber, - eventBridgeConfig: { - durable: false, - autoacknowledge: true, - shared: true, - }, - } - - const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) - - const result = isMessageMatchingSubscription(getLoggerMock().mock, getTestMessage(), storageEntry) - - expect(result).toBeTruthy() - }) - - it('fails on different sender instanceId', () => { - const subscription: Subscription = { - sender: { - ...sender, - instanceId: 'otherInstanceId', - }, - subscriber, - eventBridgeConfig: { - durable: false, - autoacknowledge: true, - shared: true, - }, - } - - const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) - - const result = isMessageMatchingSubscription(getLoggerMock().mock, getTestMessage(), storageEntry) - - expect(result).toBeFalsy() - }) - - it('matches on receiver instanceId', () => { - const subscription: Subscription = { - receiver: { - instanceId: 'ReceiverServiceInstance', - }, - subscriber, - eventBridgeConfig: { - durable: false, - autoacknowledge: true, - shared: true, - }, - } - - const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) - - const result = isMessageMatchingSubscription(getLoggerMock().mock, getTestMessage(), storageEntry) - - expect(result).toBeTruthy() - }) - - it('fails on different receiver instanceId', () => { - const subscription: Subscription = { - receiver: { - ...receiver, - instanceId: 'otherInstanceId', - }, - subscriber, - eventBridgeConfig: { - durable: false, - autoacknowledge: true, - shared: true, - }, - } - - const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) - - const result = isMessageMatchingSubscription(getLoggerMock().mock, getTestMessage(), storageEntry) - - expect(result).toBeFalsy() - }) + const sender = { + serviceName: 'SenderService', + serviceVersion: '1', + serviceTarget: 'senderServiceTarget', + instanceId: 'SenderServiceInstance', + } + + const receiver = { + serviceName: 'ReceiverService', + serviceVersion: '2', + serviceTarget: 'receiverServiceTarget', + instanceId: 'ReceiverServiceInstance', + } + + const subscriber = { + serviceName: 'SubscriberService', + serviceVersion: '3', + serviceTarget: 'subscriberServiceTarget', + } + + const callback = stub().resolves() + + const eventName = 'testEventName' + + const getTestMessage = (): EBMessage => { + return { + sender, + receiver, + payload: {}, + messageType: EBMessageType.CommandSuccessResponse, + id: 'messageTestId', + traceId: 'messageTraceId', + timestamp: Date.now(), + correlationId: 'messageCorrelationId', + principalId: 'messagePrincipalId', + tenantId: 'messageTenantId', + eventName, + contentType: 'application/json', + contentEncoding: 'utf-8', + } + } + + it('matches on sender instanceId', () => { + const subscription: Subscription = { + sender: { + instanceId: 'SenderServiceInstance', + }, + subscriber, + eventBridgeConfig: { + durable: false, + autoacknowledge: true, + shared: true, + }, + } + + const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) + + const result = isMessageMatchingSubscription(getLoggerMock().mock, getTestMessage(), storageEntry) + + expect(result).toBeTruthy() + }) + + it('fails on different sender instanceId', () => { + const subscription: Subscription = { + sender: { + ...sender, + instanceId: 'otherInstanceId', + }, + subscriber, + eventBridgeConfig: { + durable: false, + autoacknowledge: true, + shared: true, + }, + } + + const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) + + const result = isMessageMatchingSubscription(getLoggerMock().mock, getTestMessage(), storageEntry) + + expect(result).toBeFalsy() + }) + + it('matches on receiver instanceId', () => { + const subscription: Subscription = { + receiver: { + instanceId: 'ReceiverServiceInstance', + }, + subscriber, + eventBridgeConfig: { + durable: false, + autoacknowledge: true, + shared: true, + }, + } + + const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) + + const result = isMessageMatchingSubscription(getLoggerMock().mock, getTestMessage(), storageEntry) + + expect(result).toBeTruthy() + }) + + it('fails on different receiver instanceId', () => { + const subscription: Subscription = { + receiver: { + ...receiver, + instanceId: 'otherInstanceId', + }, + subscriber, + eventBridgeConfig: { + durable: false, + autoacknowledge: true, + shared: true, + }, + } + + const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) + + const result = isMessageMatchingSubscription(getLoggerMock().mock, getTestMessage(), storageEntry) + + expect(result).toBeFalsy() + }) }) diff --git a/packages/core/src/DefaultEventBridge/test/singleMatchingMessageType.test.ts b/packages/core/src/DefaultEventBridge/test/singleMatchingMessageType.test.ts index 8d70a928a..05b377da0 100644 --- a/packages/core/src/DefaultEventBridge/test/singleMatchingMessageType.test.ts +++ b/packages/core/src/DefaultEventBridge/test/singleMatchingMessageType.test.ts @@ -7,119 +7,119 @@ import { getNewSubscriptionStorageEntry } from '../getNewSubscriptionStorageEntr import { isMessageMatchingSubscription } from '../isMessageMatchingSubscription.impl.js' describe('subscription matching for message type', () => { - const sender = { - serviceName: 'SenderService', - serviceVersion: '1', - serviceTarget: 'senderServiceTarget', - instanceId: 'SenderServiceInstance', - } - - const receiver = { - serviceName: 'ReceiverService', - serviceVersion: '2', - serviceTarget: 'receiverServiceTarget', - instanceId: 'ReceiverServiceInstance', - } - - const subscriber = { - serviceName: 'SubscriberService', - serviceVersion: '3', - serviceTarget: 'subscriberServiceTarget', - instanceId: 'instanceId', - } - - const callback = stub().resolves() - - const eventName = 'testEventName' - - const getTestMessage = (): EBMessage => { - return { - sender, - receiver, - payload: {}, - messageType: EBMessageType.CommandSuccessResponse, - id: 'messageTestId', - traceId: 'messageTraceId', - timestamp: Date.now(), - correlationId: 'messageCorrelationId', - principalId: 'messagePrincipalId', - tenantId: 'messageTenantId', - eventName, - contentType: 'application/json', - contentEncoding: 'utf-8', - } - } - - it('matches on message type', () => { - const subscription: Subscription = { - messageType: EBMessageType.Command, - subscriber, - eventBridgeConfig: { - durable: false, - autoacknowledge: true, - shared: true, - }, - } - - const message: Command = { - id: 'messageTestId', - traceId: 'messageTraceId', - timestamp: Date.now(), - correlationId: 'messageCorrelationId', - principalId: 'messagePrincipalId', - tenantId: 'messageTenantId', - eventName, - sender, - receiver, - payload: { parameter: {}, payload: {} }, - messageType: EBMessageType.Command, - contentType: 'application/json', - contentEncoding: 'utf-8', - } - - const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) - - const result = isMessageMatchingSubscription(getLoggerMock().mock, message, storageEntry) - - expect(result).toBeTruthy() - }) - - it('fails on different message type', () => { - const subscription: Subscription = { - messageType: EBMessageType.InfoServiceDrain, - subscriber, - eventBridgeConfig: { - durable: false, - autoacknowledge: true, - shared: true, - }, - } - - const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) - - const result = isMessageMatchingSubscription(getLoggerMock().mock, getTestMessage(), storageEntry) - - expect(result).toBeFalsy() - }) - - it('fails on unknown message type', () => { - const subscription: Subscription = { - sender, - subscriber, - eventBridgeConfig: { - durable: false, - autoacknowledge: true, - shared: true, - }, - } - - const message = getTestMessage() - message.messageType = 'unknown' as EBMessageType - - const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) - - const result = isMessageMatchingSubscription(getLoggerMock().mock, message, storageEntry) - - expect(result).toBeFalsy() - }) + const sender = { + serviceName: 'SenderService', + serviceVersion: '1', + serviceTarget: 'senderServiceTarget', + instanceId: 'SenderServiceInstance', + } + + const receiver = { + serviceName: 'ReceiverService', + serviceVersion: '2', + serviceTarget: 'receiverServiceTarget', + instanceId: 'ReceiverServiceInstance', + } + + const subscriber = { + serviceName: 'SubscriberService', + serviceVersion: '3', + serviceTarget: 'subscriberServiceTarget', + instanceId: 'instanceId', + } + + const callback = stub().resolves() + + const eventName = 'testEventName' + + const getTestMessage = (): EBMessage => { + return { + sender, + receiver, + payload: {}, + messageType: EBMessageType.CommandSuccessResponse, + id: 'messageTestId', + traceId: 'messageTraceId', + timestamp: Date.now(), + correlationId: 'messageCorrelationId', + principalId: 'messagePrincipalId', + tenantId: 'messageTenantId', + eventName, + contentType: 'application/json', + contentEncoding: 'utf-8', + } + } + + it('matches on message type', () => { + const subscription: Subscription = { + messageType: EBMessageType.Command, + subscriber, + eventBridgeConfig: { + durable: false, + autoacknowledge: true, + shared: true, + }, + } + + const message: Command = { + id: 'messageTestId', + traceId: 'messageTraceId', + timestamp: Date.now(), + correlationId: 'messageCorrelationId', + principalId: 'messagePrincipalId', + tenantId: 'messageTenantId', + eventName, + sender, + receiver, + payload: { parameter: {}, payload: {} }, + messageType: EBMessageType.Command, + contentType: 'application/json', + contentEncoding: 'utf-8', + } + + const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) + + const result = isMessageMatchingSubscription(getLoggerMock().mock, message, storageEntry) + + expect(result).toBeTruthy() + }) + + it('fails on different message type', () => { + const subscription: Subscription = { + messageType: EBMessageType.InfoServiceDrain, + subscriber, + eventBridgeConfig: { + durable: false, + autoacknowledge: true, + shared: true, + }, + } + + const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) + + const result = isMessageMatchingSubscription(getLoggerMock().mock, getTestMessage(), storageEntry) + + expect(result).toBeFalsy() + }) + + it('fails on unknown message type', () => { + const subscription: Subscription = { + sender, + subscriber, + eventBridgeConfig: { + durable: false, + autoacknowledge: true, + shared: true, + }, + } + + const message = getTestMessage() + message.messageType = 'unknown' as EBMessageType + + const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) + + const result = isMessageMatchingSubscription(getLoggerMock().mock, message, storageEntry) + + expect(result).toBeFalsy() + }) }) diff --git a/packages/core/src/DefaultEventBridge/test/singleMatchingPrincipalId.test.ts b/packages/core/src/DefaultEventBridge/test/singleMatchingPrincipalId.test.ts index e0e2c0cd0..56e1880ae 100644 --- a/packages/core/src/DefaultEventBridge/test/singleMatchingPrincipalId.test.ts +++ b/packages/core/src/DefaultEventBridge/test/singleMatchingPrincipalId.test.ts @@ -7,82 +7,82 @@ import { getNewSubscriptionStorageEntry } from '../getNewSubscriptionStorageEntr import { isMessageMatchingSubscription } from '../isMessageMatchingSubscription.impl.js' describe('subscription matching for principalId', () => { - const sender = { - serviceName: 'SenderService', - serviceVersion: '1', - serviceTarget: 'senderServiceTarget', - instanceId: 'SenderServiceInstance', - } + const sender = { + serviceName: 'SenderService', + serviceVersion: '1', + serviceTarget: 'senderServiceTarget', + instanceId: 'SenderServiceInstance', + } - const receiver = { - serviceName: 'ReceiverService', - serviceVersion: '2', - serviceTarget: 'receiverServiceTarget', - instanceId: 'ReceiverServiceInstance', - } + const receiver = { + serviceName: 'ReceiverService', + serviceVersion: '2', + serviceTarget: 'receiverServiceTarget', + instanceId: 'ReceiverServiceInstance', + } - const subscriber = { - serviceName: 'SubscriberService', - serviceVersion: '3', - serviceTarget: 'subscriberServiceTarget', - instanceId: 'instanceId', - } + const subscriber = { + serviceName: 'SubscriberService', + serviceVersion: '3', + serviceTarget: 'subscriberServiceTarget', + instanceId: 'instanceId', + } - const callback = stub().resolves() + const callback = stub().resolves() - const eventName = 'testEventName' + const eventName = 'testEventName' - const getTestMessage = (): EBMessage => { - return { - sender, - receiver, - payload: {}, - messageType: EBMessageType.CommandSuccessResponse, - id: 'messageTestId', - traceId: 'messageTraceId', - timestamp: Date.now(), - correlationId: 'messageCorrelationId', - principalId: 'messagePrincipalId', - tenantId: 'messageTenantId', - eventName, - contentType: 'application/json', - contentEncoding: 'utf-8', - } - } + const getTestMessage = (): EBMessage => { + return { + sender, + receiver, + payload: {}, + messageType: EBMessageType.CommandSuccessResponse, + id: 'messageTestId', + traceId: 'messageTraceId', + timestamp: Date.now(), + correlationId: 'messageCorrelationId', + principalId: 'messagePrincipalId', + tenantId: 'messageTenantId', + eventName, + contentType: 'application/json', + contentEncoding: 'utf-8', + } + } - it('matches on principalId', () => { - const subscription: Subscription = { - principalId: 'messagePrincipalId', - subscriber, - eventBridgeConfig: { - durable: false, - autoacknowledge: true, - shared: true, - }, - } + it('matches on principalId', () => { + const subscription: Subscription = { + principalId: 'messagePrincipalId', + subscriber, + eventBridgeConfig: { + durable: false, + autoacknowledge: true, + shared: true, + }, + } - const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) + const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) - const result = isMessageMatchingSubscription(getLoggerMock().mock, getTestMessage(), storageEntry) + const result = isMessageMatchingSubscription(getLoggerMock().mock, getTestMessage(), storageEntry) - expect(result).toBeTruthy() - }) + expect(result).toBeTruthy() + }) - it('fails on different principalId', () => { - const subscription: Subscription = { - principalId: 'otherPrincipalId', - subscriber, - eventBridgeConfig: { - durable: false, - autoacknowledge: true, - shared: true, - }, - } + it('fails on different principalId', () => { + const subscription: Subscription = { + principalId: 'otherPrincipalId', + subscriber, + eventBridgeConfig: { + durable: false, + autoacknowledge: true, + shared: true, + }, + } - const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) + const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) - const result = isMessageMatchingSubscription(getLoggerMock().mock, getTestMessage(), storageEntry) + const result = isMessageMatchingSubscription(getLoggerMock().mock, getTestMessage(), storageEntry) - expect(result).toBeFalsy() - }) + expect(result).toBeFalsy() + }) }) diff --git a/packages/core/src/DefaultEventBridge/test/singleMatchingReceiver.test.ts b/packages/core/src/DefaultEventBridge/test/singleMatchingReceiver.test.ts index 49a44c9a7..937e73a23 100644 --- a/packages/core/src/DefaultEventBridge/test/singleMatchingReceiver.test.ts +++ b/packages/core/src/DefaultEventBridge/test/singleMatchingReceiver.test.ts @@ -7,166 +7,166 @@ import { getNewSubscriptionStorageEntry } from '../getNewSubscriptionStorageEntr import { isMessageMatchingSubscription } from '../isMessageMatchingSubscription.impl.js' describe('subscription matching for sender', () => { - const sender = { - serviceName: 'SenderService', - serviceVersion: '1', - serviceTarget: 'senderServiceTarget', - instanceId: 'instanceId', - } - - const receiver = { - serviceName: 'ReceiverService', - serviceVersion: '2', - serviceTarget: 'receiverServiceTarget', - instanceId: 'instanceId', - } - - const subscriber = { - serviceName: 'SubscriberService', - serviceVersion: '3', - serviceTarget: 'subscriberServiceTarget', - instanceId: 'instanceId', - } - - const callback = stub().resolves() - - const eventName = 'testEventName' - - const getTestMessage = (): EBMessage => { - return { - sender, - receiver, - payload: {}, - messageType: EBMessageType.CommandSuccessResponse, - id: 'messageTestId', - traceId: 'messageTraceId', - timestamp: Date.now(), - correlationId: 'messageCorrelationId', - principalId: 'messagePrincipalId', - tenantId: 'messageTenantId', - eventName, - contentType: 'application/json', - contentEncoding: 'utf-8', - } - } - - it('matches on receiver service name', () => { - const subscription: Subscription = { - receiver: { - serviceName: receiver.serviceName, - }, - subscriber, - eventBridgeConfig: { - durable: false, - autoacknowledge: true, - shared: true, - }, - } - - const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) - - const result = isMessageMatchingSubscription(getLoggerMock().mock, getTestMessage(), storageEntry) - - expect(result).toBeTruthy() - }) - - it('fails on different receiver service name', () => { - const subscription: Subscription = { - receiver: { - serviceName: 'differentService', - }, - subscriber, - eventBridgeConfig: { - durable: false, - autoacknowledge: true, - shared: true, - }, - } - - const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) - - const result = isMessageMatchingSubscription(getLoggerMock().mock, getTestMessage(), storageEntry) - - expect(result).toBeFalsy() - }) - - it('matches on receiver service version', () => { - const subscription: Subscription = { - receiver: { - serviceVersion: receiver.serviceVersion, - }, - subscriber, - eventBridgeConfig: { - durable: false, - autoacknowledge: true, - shared: true, - }, - } - - const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) - - const result = isMessageMatchingSubscription(getLoggerMock().mock, getTestMessage(), storageEntry) - - expect(result).toBeTruthy() - }) - - it('fails on different receiver service version', () => { - const subscription: Subscription = { - receiver: { - serviceVersion: '9', - }, - subscriber, - eventBridgeConfig: { - durable: false, - autoacknowledge: true, - shared: true, - }, - } - - const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) - - const result = isMessageMatchingSubscription(getLoggerMock().mock, getTestMessage(), storageEntry) - - expect(result).toBeFalsy() - }) - - it('matches on receiver service target', () => { - const subscription: Subscription = { - receiver: { - serviceTarget: receiver.serviceTarget, - }, - subscriber, - eventBridgeConfig: { - durable: false, - autoacknowledge: true, - shared: true, - }, - } - - const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) - - const result = isMessageMatchingSubscription(getLoggerMock().mock, getTestMessage(), storageEntry) - - expect(result).toBeTruthy() - }) - - it('fails on different receiver service target', () => { - const subscription: Subscription = { - receiver: { - serviceTarget: 'differentTarget', - }, - subscriber, - eventBridgeConfig: { - durable: false, - autoacknowledge: true, - shared: true, - }, - } - - const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) - - const result = isMessageMatchingSubscription(getLoggerMock().mock, getTestMessage(), storageEntry) - - expect(result).toBeFalsy() - }) + const sender = { + serviceName: 'SenderService', + serviceVersion: '1', + serviceTarget: 'senderServiceTarget', + instanceId: 'instanceId', + } + + const receiver = { + serviceName: 'ReceiverService', + serviceVersion: '2', + serviceTarget: 'receiverServiceTarget', + instanceId: 'instanceId', + } + + const subscriber = { + serviceName: 'SubscriberService', + serviceVersion: '3', + serviceTarget: 'subscriberServiceTarget', + instanceId: 'instanceId', + } + + const callback = stub().resolves() + + const eventName = 'testEventName' + + const getTestMessage = (): EBMessage => { + return { + sender, + receiver, + payload: {}, + messageType: EBMessageType.CommandSuccessResponse, + id: 'messageTestId', + traceId: 'messageTraceId', + timestamp: Date.now(), + correlationId: 'messageCorrelationId', + principalId: 'messagePrincipalId', + tenantId: 'messageTenantId', + eventName, + contentType: 'application/json', + contentEncoding: 'utf-8', + } + } + + it('matches on receiver service name', () => { + const subscription: Subscription = { + receiver: { + serviceName: receiver.serviceName, + }, + subscriber, + eventBridgeConfig: { + durable: false, + autoacknowledge: true, + shared: true, + }, + } + + const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) + + const result = isMessageMatchingSubscription(getLoggerMock().mock, getTestMessage(), storageEntry) + + expect(result).toBeTruthy() + }) + + it('fails on different receiver service name', () => { + const subscription: Subscription = { + receiver: { + serviceName: 'differentService', + }, + subscriber, + eventBridgeConfig: { + durable: false, + autoacknowledge: true, + shared: true, + }, + } + + const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) + + const result = isMessageMatchingSubscription(getLoggerMock().mock, getTestMessage(), storageEntry) + + expect(result).toBeFalsy() + }) + + it('matches on receiver service version', () => { + const subscription: Subscription = { + receiver: { + serviceVersion: receiver.serviceVersion, + }, + subscriber, + eventBridgeConfig: { + durable: false, + autoacknowledge: true, + shared: true, + }, + } + + const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) + + const result = isMessageMatchingSubscription(getLoggerMock().mock, getTestMessage(), storageEntry) + + expect(result).toBeTruthy() + }) + + it('fails on different receiver service version', () => { + const subscription: Subscription = { + receiver: { + serviceVersion: '9', + }, + subscriber, + eventBridgeConfig: { + durable: false, + autoacknowledge: true, + shared: true, + }, + } + + const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) + + const result = isMessageMatchingSubscription(getLoggerMock().mock, getTestMessage(), storageEntry) + + expect(result).toBeFalsy() + }) + + it('matches on receiver service target', () => { + const subscription: Subscription = { + receiver: { + serviceTarget: receiver.serviceTarget, + }, + subscriber, + eventBridgeConfig: { + durable: false, + autoacknowledge: true, + shared: true, + }, + } + + const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) + + const result = isMessageMatchingSubscription(getLoggerMock().mock, getTestMessage(), storageEntry) + + expect(result).toBeTruthy() + }) + + it('fails on different receiver service target', () => { + const subscription: Subscription = { + receiver: { + serviceTarget: 'differentTarget', + }, + subscriber, + eventBridgeConfig: { + durable: false, + autoacknowledge: true, + shared: true, + }, + } + + const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) + + const result = isMessageMatchingSubscription(getLoggerMock().mock, getTestMessage(), storageEntry) + + expect(result).toBeFalsy() + }) }) diff --git a/packages/core/src/DefaultEventBridge/test/singleMatchingSender.test.ts b/packages/core/src/DefaultEventBridge/test/singleMatchingSender.test.ts index 90c7a8f59..ef29b7fd4 100644 --- a/packages/core/src/DefaultEventBridge/test/singleMatchingSender.test.ts +++ b/packages/core/src/DefaultEventBridge/test/singleMatchingSender.test.ts @@ -7,166 +7,166 @@ import { getNewSubscriptionStorageEntry } from '../getNewSubscriptionStorageEntr import { isMessageMatchingSubscription } from '../isMessageMatchingSubscription.impl.js' describe('subscription matching for sender', () => { - const sender = { - serviceName: 'SenderService', - serviceVersion: '1', - serviceTarget: 'senderServiceTarget', - instanceId: 'SenderServiceInstance', - } - - const receiver = { - serviceName: 'ReceiverService', - serviceVersion: '2', - serviceTarget: 'receiverServiceTarget', - instanceId: 'ReceiverServiceInstance', - } - - const subscriber = { - serviceName: 'SubscriberService', - serviceVersion: '3', - serviceTarget: 'subscriberServiceTarget', - instanceId: 'instanceId', - } - - const callback = stub().resolves() - - const eventName = 'testEventName' - - const getTestMessage = (): EBMessage => { - return { - sender, - receiver, - payload: {}, - messageType: EBMessageType.CommandSuccessResponse, - id: 'messageTestId', - traceId: 'messageTraceId', - timestamp: Date.now(), - correlationId: 'messageCorrelationId', - principalId: 'messagePrincipalId', - tenantId: 'messageTenantId', - eventName, - contentType: 'application/json', - contentEncoding: 'utf-8', - } - } - - it('matches on sender service name', () => { - const subscription: Subscription = { - sender: { - serviceName: sender.serviceName, - }, - subscriber, - eventBridgeConfig: { - durable: false, - autoacknowledge: true, - shared: true, - }, - } - - const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) - - const result = isMessageMatchingSubscription(getLoggerMock().mock, getTestMessage(), storageEntry) - - expect(result).toBeTruthy() - }) - - it('fails on different sender service name', () => { - const subscription: Subscription = { - sender: { - serviceName: 'differentService', - }, - subscriber, - eventBridgeConfig: { - durable: false, - autoacknowledge: true, - shared: true, - }, - } - - const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) - - const result = isMessageMatchingSubscription(getLoggerMock().mock, getTestMessage(), storageEntry) - - expect(result).toBeFalsy() - }) - - it('matches on sender service version', () => { - const subscription: Subscription = { - sender: { - serviceVersion: sender.serviceVersion, - }, - subscriber, - eventBridgeConfig: { - durable: false, - autoacknowledge: true, - shared: true, - }, - } - - const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) - - const result = isMessageMatchingSubscription(getLoggerMock().mock, getTestMessage(), storageEntry) - - expect(result).toBeTruthy() - }) - - it('fails on different sender service version', () => { - const subscription: Subscription = { - sender: { - serviceVersion: '9', - }, - subscriber, - eventBridgeConfig: { - durable: false, - autoacknowledge: true, - shared: true, - }, - } - - const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) - - const result = isMessageMatchingSubscription(getLoggerMock().mock, getTestMessage(), storageEntry) - - expect(result).toBeFalsy() - }) - - it('matches on sender service target', () => { - const subscription: Subscription = { - sender: { - serviceTarget: sender.serviceTarget, - }, - subscriber, - eventBridgeConfig: { - durable: false, - autoacknowledge: true, - shared: true, - }, - } - - const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) - - const result = isMessageMatchingSubscription(getLoggerMock().mock, getTestMessage(), storageEntry) - - expect(result).toBeTruthy() - }) - - it('fails on different sender service target', () => { - const subscription: Subscription = { - sender: { - serviceTarget: 'differentTarget', - }, - subscriber, - eventBridgeConfig: { - durable: false, - autoacknowledge: true, - shared: true, - }, - } - - const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) - - const result = isMessageMatchingSubscription(getLoggerMock().mock, getTestMessage(), storageEntry) - - expect(result).toBeFalsy() - }) + const sender = { + serviceName: 'SenderService', + serviceVersion: '1', + serviceTarget: 'senderServiceTarget', + instanceId: 'SenderServiceInstance', + } + + const receiver = { + serviceName: 'ReceiverService', + serviceVersion: '2', + serviceTarget: 'receiverServiceTarget', + instanceId: 'ReceiverServiceInstance', + } + + const subscriber = { + serviceName: 'SubscriberService', + serviceVersion: '3', + serviceTarget: 'subscriberServiceTarget', + instanceId: 'instanceId', + } + + const callback = stub().resolves() + + const eventName = 'testEventName' + + const getTestMessage = (): EBMessage => { + return { + sender, + receiver, + payload: {}, + messageType: EBMessageType.CommandSuccessResponse, + id: 'messageTestId', + traceId: 'messageTraceId', + timestamp: Date.now(), + correlationId: 'messageCorrelationId', + principalId: 'messagePrincipalId', + tenantId: 'messageTenantId', + eventName, + contentType: 'application/json', + contentEncoding: 'utf-8', + } + } + + it('matches on sender service name', () => { + const subscription: Subscription = { + sender: { + serviceName: sender.serviceName, + }, + subscriber, + eventBridgeConfig: { + durable: false, + autoacknowledge: true, + shared: true, + }, + } + + const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) + + const result = isMessageMatchingSubscription(getLoggerMock().mock, getTestMessage(), storageEntry) + + expect(result).toBeTruthy() + }) + + it('fails on different sender service name', () => { + const subscription: Subscription = { + sender: { + serviceName: 'differentService', + }, + subscriber, + eventBridgeConfig: { + durable: false, + autoacknowledge: true, + shared: true, + }, + } + + const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) + + const result = isMessageMatchingSubscription(getLoggerMock().mock, getTestMessage(), storageEntry) + + expect(result).toBeFalsy() + }) + + it('matches on sender service version', () => { + const subscription: Subscription = { + sender: { + serviceVersion: sender.serviceVersion, + }, + subscriber, + eventBridgeConfig: { + durable: false, + autoacknowledge: true, + shared: true, + }, + } + + const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) + + const result = isMessageMatchingSubscription(getLoggerMock().mock, getTestMessage(), storageEntry) + + expect(result).toBeTruthy() + }) + + it('fails on different sender service version', () => { + const subscription: Subscription = { + sender: { + serviceVersion: '9', + }, + subscriber, + eventBridgeConfig: { + durable: false, + autoacknowledge: true, + shared: true, + }, + } + + const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) + + const result = isMessageMatchingSubscription(getLoggerMock().mock, getTestMessage(), storageEntry) + + expect(result).toBeFalsy() + }) + + it('matches on sender service target', () => { + const subscription: Subscription = { + sender: { + serviceTarget: sender.serviceTarget, + }, + subscriber, + eventBridgeConfig: { + durable: false, + autoacknowledge: true, + shared: true, + }, + } + + const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) + + const result = isMessageMatchingSubscription(getLoggerMock().mock, getTestMessage(), storageEntry) + + expect(result).toBeTruthy() + }) + + it('fails on different sender service target', () => { + const subscription: Subscription = { + sender: { + serviceTarget: 'differentTarget', + }, + subscriber, + eventBridgeConfig: { + durable: false, + autoacknowledge: true, + shared: true, + }, + } + + const storageEntry = getNewSubscriptionStorageEntry(subscription, callback) + + const result = isMessageMatchingSubscription(getLoggerMock().mock, getTestMessage(), storageEntry) + + expect(result).toBeFalsy() + }) }) diff --git a/packages/core/src/DefaultEventBridge/types/DefaultEventBridgeConfig.ts b/packages/core/src/DefaultEventBridge/types/DefaultEventBridgeConfig.ts index dd03cc59c..8bcf68fe1 100644 --- a/packages/core/src/DefaultEventBridge/types/DefaultEventBridgeConfig.ts +++ b/packages/core/src/DefaultEventBridge/types/DefaultEventBridgeConfig.ts @@ -2,9 +2,9 @@ * The configuration for the DefaultEventBridge. */ export type DefaultEventBridgeConfig = { - /** Log warnings on messages which are emitted, but could not delivered to at least one receiver */ - logWarnOnMessagesWithoutReceiver?: boolean + /** Log warnings on messages which are emitted, but could not delivered to at least one receiver */ + logWarnOnMessagesWithoutReceiver?: boolean - /** Emit messages which have an event name set as javascript events on the event bridge instance */ - emitMessagesAsEventBridgeEvents?: boolean + /** Emit messages which have an event name set as javascript events on the event bridge instance */ + emitMessagesAsEventBridgeEvents?: boolean } diff --git a/packages/core/src/DefaultEventBridge/types/PendingInvocations.ts b/packages/core/src/DefaultEventBridge/types/PendingInvocations.ts index 933ff3270..09bf5a3d3 100644 --- a/packages/core/src/DefaultEventBridge/types/PendingInvocations.ts +++ b/packages/core/src/DefaultEventBridge/types/PendingInvocations.ts @@ -1,6 +1,6 @@ import type { HandledError, UnhandledError } from '../../core/index.js' export type PendigInvocation = { - resolve(responsePayload: unknown): void - reject(error: UnhandledError | HandledError): void + resolve(responsePayload: unknown): void + reject(error: UnhandledError | HandledError): void } diff --git a/packages/core/src/DefaultEventBridge/types/SubscriptionStorageEntry.ts b/packages/core/src/DefaultEventBridge/types/SubscriptionStorageEntry.ts index fe53db317..3acd16cfc 100644 --- a/packages/core/src/DefaultEventBridge/types/SubscriptionStorageEntry.ts +++ b/packages/core/src/DefaultEventBridge/types/SubscriptionStorageEntry.ts @@ -1,18 +1,18 @@ import type { CustomMessage, EBMessage, EBMessageType, InstanceId, PrincipalId, TenantId } from '../../core/index.js' export type SubscriptionStorageEntry = { - isMatchingMessageType(input: EBMessageType): boolean - isMatchingSenderServiceName(input?: string): boolean - isMatchingSenderServiceVersion(input?: string): boolean - isMatchingSenderServiceTarget(input?: string): boolean - isMatchingSenderInstanceId(input?: InstanceId): boolean - isMatchingReceiverServiceName(input?: string): boolean - isMatchingReceiverServiceVersion(input?: string): boolean - isMatchingReceiverServiceTarget(input?: string): boolean - isMatchingReceiverInstanceId(input?: InstanceId): boolean - isMatchingEventName(input?: string): boolean - isMatchingPrincipalId(input?: PrincipalId): boolean - isMatchingTenantId(input?: TenantId): boolean - emitEventName?: string - cb: (message: EBMessage) => Promise | undefined> + isMatchingMessageType(input: EBMessageType): boolean + isMatchingSenderServiceName(input?: string): boolean + isMatchingSenderServiceVersion(input?: string): boolean + isMatchingSenderServiceTarget(input?: string): boolean + isMatchingSenderInstanceId(input?: InstanceId): boolean + isMatchingReceiverServiceName(input?: string): boolean + isMatchingReceiverServiceVersion(input?: string): boolean + isMatchingReceiverServiceTarget(input?: string): boolean + isMatchingReceiverInstanceId(input?: InstanceId): boolean + isMatchingEventName(input?: string): boolean + isMatchingPrincipalId(input?: PrincipalId): boolean + isMatchingTenantId(input?: TenantId): boolean + emitEventName?: string + cb: (message: EBMessage) => Promise | undefined> } diff --git a/packages/core/src/DefaultLogger/DefaultLogger.impl.ts b/packages/core/src/DefaultLogger/DefaultLogger.impl.ts index 9bb5afd61..9e483d8c8 100644 --- a/packages/core/src/DefaultLogger/DefaultLogger.impl.ts +++ b/packages/core/src/DefaultLogger/DefaultLogger.impl.ts @@ -4,68 +4,68 @@ import type { ILogger, LogFnParamType, LoggerOptions } from '../core/types/index import { Logger } from '../core/types/index.js' export class DefaultLogger extends Logger implements ILogger { - constructor(private log: PinoLogger) { - super() - } + constructor(private log: PinoLogger) { + super() + } - fatal(...args: LogFnParamType) { - if (args.length === 1) { - this.log.fatal(args[0]) - return - } - this.log.fatal(args[0], ...args.slice(1)) - } + fatal(...args: LogFnParamType) { + if (args.length === 1) { + this.log.fatal(args[0]) + return + } + this.log.fatal(args[0], ...args.slice(1)) + } - error(...args: LogFnParamType) { - if (args.length === 1) { - this.log.error(args[0]) - return - } - this.log.error(args[0], ...args.slice(1)) - } + error(...args: LogFnParamType) { + if (args.length === 1) { + this.log.error(args[0]) + return + } + this.log.error(args[0], ...args.slice(1)) + } - warn(...args: LogFnParamType) { - if (args.length === 1) { - this.log.warn(args[0]) - return - } - this.log.warn(args[0], ...args.slice(1)) - } + warn(...args: LogFnParamType) { + if (args.length === 1) { + this.log.warn(args[0]) + return + } + this.log.warn(args[0], ...args.slice(1)) + } - info(...args: LogFnParamType) { - if (args.length === 1) { - this.log.info(args[0]) - return - } - this.log.info(args[0], ...args.slice(1)) - } + info(...args: LogFnParamType) { + if (args.length === 1) { + this.log.info(args[0]) + return + } + this.log.info(args[0], ...args.slice(1)) + } - debug(...args: LogFnParamType) { - if (args.length === 1) { - this.log.debug(args[0]) - return - } - this.log.debug(args[0], ...args.slice(1)) - } + debug(...args: LogFnParamType) { + if (args.length === 1) { + this.log.debug(args[0]) + return + } + this.log.debug(args[0], ...args.slice(1)) + } - trace(...args: LogFnParamType) { - if (args.length === 1) { - this.log.trace(args[0]) - return - } - this.log.trace(args[0], ...args.slice(1)) - } + trace(...args: LogFnParamType) { + if (args.length === 1) { + this.log.trace(args[0]) + return + } + this.log.trace(args[0], ...args.slice(1)) + } - getChildLogger(options: LoggerOptions): Logger { - const prefix = [options.serviceName, options.serviceVersion, options.serviceTarget].filter((entry) => !!entry) + getChildLogger(options: LoggerOptions): Logger { + const prefix = [options.serviceName, options.serviceVersion, options.serviceTarget].filter(entry => !!entry) - const parameter: LoggerOptions = { - ...options, - name: undefined, - module: options.module ?? options.name ?? prefix.join('-'), - } + const parameter: LoggerOptions = { + ...options, + name: undefined, + module: options.module ?? options.name ?? prefix.join('-'), + } - const child = this.log.child(parameter) - return new DefaultLogger(child) - } + const child = this.log.child(parameter) + return new DefaultLogger(child) + } } diff --git a/packages/core/src/DefaultLogger/defaultLogger.test.ts b/packages/core/src/DefaultLogger/defaultLogger.test.ts index 670399805..90e93ccde 100644 --- a/packages/core/src/DefaultLogger/defaultLogger.test.ts +++ b/packages/core/src/DefaultLogger/defaultLogger.test.ts @@ -5,66 +5,66 @@ import type { Logger, LoggerOptions } from '../core/types/index.js' import { DefaultLogger } from './DefaultLogger.impl.js' describe('DefaultLogger', () => { - let sandbox: SinonSandbox - let mockLog: any - let logger: Logger + let sandbox: SinonSandbox + let mockLog: any + let logger: Logger - beforeEach(() => { - sandbox = createSandbox() - mockLog = { - fatal: sandbox.stub(), - error: sandbox.stub(), - warn: sandbox.stub(), - info: sandbox.stub(), - debug: sandbox.stub(), - trace: sandbox.stub(), - child: sandbox.stub().returnsThis(), - } - logger = new DefaultLogger(mockLog) - }) + beforeEach(() => { + sandbox = createSandbox() + mockLog = { + fatal: sandbox.stub(), + error: sandbox.stub(), + warn: sandbox.stub(), + info: sandbox.stub(), + debug: sandbox.stub(), + trace: sandbox.stub(), + child: sandbox.stub().returnsThis(), + } + logger = new DefaultLogger(mockLog) + }) - afterEach(() => { - sandbox.restore() - }) + afterEach(() => { + sandbox.restore() + }) - it('should log a fatal message', () => { - logger.fatal('fatal error') - expect(mockLog.fatal.calledOnceWithExactly('fatal error')).toBeTruthy() - }) + it('should log a fatal message', () => { + logger.fatal('fatal error') + expect(mockLog.fatal.calledOnceWithExactly('fatal error')).toBeTruthy() + }) - it('should log an error message', () => { - logger.error('error message') - expect(mockLog.error.calledOnceWithExactly('error message')).toBeTruthy() - }) + it('should log an error message', () => { + logger.error('error message') + expect(mockLog.error.calledOnceWithExactly('error message')).toBeTruthy() + }) - it('should log a warning message', () => { - logger.warn('warning message') - expect(mockLog.warn.calledOnceWithExactly('warning message')).toBeTruthy() - }) + it('should log a warning message', () => { + logger.warn('warning message') + expect(mockLog.warn.calledOnceWithExactly('warning message')).toBeTruthy() + }) - it('should log an info message', () => { - logger.info('info message') - expect(mockLog.info.calledOnceWithExactly('info message')).toBeTruthy() - }) + it('should log an info message', () => { + logger.info('info message') + expect(mockLog.info.calledOnceWithExactly('info message')).toBeTruthy() + }) - it('should log a debug message', () => { - logger.debug('debug message') - expect(mockLog.debug.calledOnceWithExactly('debug message')).toBeTruthy() - }) + it('should log a debug message', () => { + logger.debug('debug message') + expect(mockLog.debug.calledOnceWithExactly('debug message')).toBeTruthy() + }) - it('should log a trace message', () => { - logger.trace('trace message') - expect(mockLog.trace.calledOnceWithExactly('trace message')).toBeTruthy() - }) + it('should log a trace message', () => { + logger.trace('trace message') + expect(mockLog.trace.calledOnceWithExactly('trace message')).toBeTruthy() + }) - it('should get a child logger', () => { - const childOptions: LoggerOptions = { - serviceName: 'test-service', - serviceVersion: '1.0.0', - serviceTarget: 'test-target', - name: 'test-name', - } - const childLogger = logger.getChildLogger(childOptions) - expect(childLogger).toBeInstanceOf(DefaultLogger) - }) + it('should get a child logger', () => { + const childOptions: LoggerOptions = { + serviceName: 'test-service', + serviceVersion: '1.0.0', + serviceTarget: 'test-target', + name: 'test-name', + } + const childLogger = logger.getChildLogger(childOptions) + expect(childLogger).toBeInstanceOf(DefaultLogger) + }) }) diff --git a/packages/core/src/DefaultLogger/getDefaultLogLevel.ts b/packages/core/src/DefaultLogger/getDefaultLogLevel.ts index f2f1b4629..0ce7f04d7 100644 --- a/packages/core/src/DefaultLogger/getDefaultLogLevel.ts +++ b/packages/core/src/DefaultLogger/getDefaultLogLevel.ts @@ -2,5 +2,5 @@ import { isDevelop } from '../core/helper/index.js' import type { LogLevelName } from '../core/types/index.js' export const getDefaultLogLevel = (): LogLevelName => { - return isDevelop() ? 'debug' : 'info' + return isDevelop() ? 'debug' : 'info' } diff --git a/packages/core/src/DefaultLogger/initLogger.impl.ts b/packages/core/src/DefaultLogger/initLogger.impl.ts index b820310be..4562fba9f 100644 --- a/packages/core/src/DefaultLogger/initLogger.impl.ts +++ b/packages/core/src/DefaultLogger/initLogger.impl.ts @@ -1,7 +1,7 @@ import type { LoggerOptions } from 'pino' import { pino } from 'pino' -import type { Logger, LogLevelName } from '../core/types/index.js' +import type { LogLevelName, Logger } from '../core/types/index.js' import { puristaVersion } from '../version.js' import { DefaultLogger } from './DefaultLogger.impl.js' import { getDefaultLogLevel } from './getDefaultLogLevel.js' @@ -12,17 +12,17 @@ import { getDefaultLogLevel } from './getDefaultLogLevel.js' * @param {LoggerOptions} */ export const initLogger = (level: LogLevelName = getDefaultLogLevel(), opt?: LoggerOptions): Logger => { - return new DefaultLogger( - pino({ - name: 'PURISTA', - mixin(context: any, _level: any) { - return { puristaVersion, ...context } - }, - mixinMergeStrategy(mergeObject: any, mixinObject: any) { - return Object.assign(mixinObject, mergeObject) - }, - ...opt, - level, - }), - ) + return new DefaultLogger( + pino({ + name: 'PURISTA', + mixin(context: any, _level: any) { + return { puristaVersion, ...context } + }, + mixinMergeStrategy(mergeObject: any, mixinObject: any) { + return Object.assign(mixinObject, mergeObject) + }, + ...opt, + level, + }), + ) } diff --git a/packages/core/src/DefaultSecretStore/DefaultSecretStore.impl.ts b/packages/core/src/DefaultSecretStore/DefaultSecretStore.impl.ts index 87a5173b7..0039273b4 100644 --- a/packages/core/src/DefaultSecretStore/DefaultSecretStore.impl.ts +++ b/packages/core/src/DefaultSecretStore/DefaultSecretStore.impl.ts @@ -35,32 +35,33 @@ import type { DefaultSecretStoreConfig } from './types/index.js' * */ export class DefaultSecretStore extends SecretStoreBaseClass implements SecretStore { - private map = new Map() - constructor(config?: StoreBaseConfig) { - super('DefaultSecretStore', { ...config }) - if (config?.config) { - this.map = new Map(Object.entries(config.config)) - } - this.logger.warn( - 'Using the DefaultSecretStore is not secure! It should only be used for test or development purpose.', - ) - } + private map = new Map() + constructor(config?: StoreBaseConfig) { + super('DefaultSecretStore', { ...config }) + if (config?.config) { + this.map = new Map(Object.entries(config.config)) + } + this.logger.warn( + 'Using the DefaultSecretStore is not secure! It should only be used for test or development purpose.', + ) + } - protected async getSecretImpl( - ...secretNames: SecretNames - ): Promise> { - const result: Record = {} - secretNames.forEach((name) => { - result[name] = this.map.get(name) - }) - return result as ObjectWithKeysFromStringArray - } + protected async getSecretImpl( + ...secretNames: SecretNames + ): Promise> { + const result: Record = {} + for (const name of secretNames) { + result[name] = this.map.get(name) + } - protected async setSecretImpl(secretName: string, secretValue: string) { - this.map.set(secretName, secretValue) - } + return result as ObjectWithKeysFromStringArray + } - protected async removeSecretImpl(secretName: string) { - this.map.delete(secretName) - } + protected async setSecretImpl(secretName: string, secretValue: string) { + this.map.set(secretName, secretValue) + } + + protected async removeSecretImpl(secretName: string) { + this.map.delete(secretName) + } } diff --git a/packages/core/src/DefaultSecretStore/defaultSecretStore.test.ts b/packages/core/src/DefaultSecretStore/defaultSecretStore.test.ts index 0d0686100..5e657e738 100644 --- a/packages/core/src/DefaultSecretStore/defaultSecretStore.test.ts +++ b/packages/core/src/DefaultSecretStore/defaultSecretStore.test.ts @@ -4,61 +4,61 @@ import { getLoggerMock } from '../mocks/index.js' import { DefaultSecretStore } from './DefaultSecretStore.impl.js' describe('DefaultSecretStore', () => { - const sandbox = createSandbox() + const sandbox = createSandbox() - afterEach(() => { - sandbox.restore() - }) + afterEach(() => { + sandbox.restore() + }) - it('throws if operation is disabled', async () => { - const logger = getLoggerMock(sandbox) - const store = new DefaultSecretStore({ - logger: logger.mock, - enableGet: false, - enableRemove: false, - enableSet: false, - }) + it('throws if operation is disabled', async () => { + const logger = getLoggerMock(sandbox) + const store = new DefaultSecretStore({ + logger: logger.mock, + enableGet: false, + enableRemove: false, + enableSet: false, + }) - await expect(store.getSecret('example')).rejects.toThrow('get secret from store is disabled by config') + await expect(store.getSecret('example')).rejects.toThrow('get secret from store is disabled by config') - await expect(store.removeSecret('example')).rejects.toThrow('remove secret from store is disabled by config') + await expect(store.removeSecret('example')).rejects.toThrow('remove secret from store is disabled by config') - await expect(store.setSecret('example', 'value')).rejects.toThrow('set secret at store is disabled by config') + await expect(store.setSecret('example', 'value')).rejects.toThrow('set secret at store is disabled by config') - expect( - logger.stubs.warn.calledWith( - 'Using the DefaultSecretStore is not secure! It should only be used for test or development purpose.', - ), - ).toBeTruthy() - }) + expect( + logger.stubs.warn.calledWith( + 'Using the DefaultSecretStore is not secure! It should only be used for test or development purpose.', + ), + ).toBeTruthy() + }) - it('handles secrets', async () => { - const logger = getLoggerMock(sandbox) - const store = new DefaultSecretStore({ - logger: logger.mock, - enableGet: true, - enableRemove: true, - enableSet: true, - config: { - initialSecret: 'initial', - }, - }) + it('handles secrets', async () => { + const logger = getLoggerMock(sandbox) + const store = new DefaultSecretStore({ + logger: logger.mock, + enableGet: true, + enableRemove: true, + enableSet: true, + config: { + initialSecret: 'initial', + }, + }) - await expect(store.getSecret('initialSecret', 'unknownSecret')).resolves.toEqual({ - initialSecret: 'initial', - unknownSecret: undefined, - }) + await expect(store.getSecret('initialSecret', 'unknownSecret')).resolves.toEqual({ + initialSecret: 'initial', + unknownSecret: undefined, + }) - await expect(store.setSecret('initialSecret', 'other_value')).resolves.toBeUndefined() + await expect(store.setSecret('initialSecret', 'other_value')).resolves.toBeUndefined() - await expect(store.getSecret('initialSecret')).resolves.toEqual({ - initialSecret: 'other_value', - }) + await expect(store.getSecret('initialSecret')).resolves.toEqual({ + initialSecret: 'other_value', + }) - await expect(store.removeSecret('initialSecret')).resolves.toBeUndefined() + await expect(store.removeSecret('initialSecret')).resolves.toBeUndefined() - await expect(store.getSecret('initialSecret')).resolves.toEqual({ - initialSecret: undefined, - }) - }) + await expect(store.getSecret('initialSecret')).resolves.toEqual({ + initialSecret: undefined, + }) + }) }) diff --git a/packages/core/src/DefaultSecretStore/initDefaultSecretStore.impl.ts b/packages/core/src/DefaultSecretStore/initDefaultSecretStore.impl.ts index 71a4ff8cf..da25b899c 100644 --- a/packages/core/src/DefaultSecretStore/initDefaultSecretStore.impl.ts +++ b/packages/core/src/DefaultSecretStore/initDefaultSecretStore.impl.ts @@ -2,6 +2,6 @@ import type { Logger } from '../core/index.js' import { DefaultSecretStore } from './DefaultSecretStore.impl.js' export const initDefaultSecretStore = (options: { logger: Logger }): DefaultSecretStore => { - const store = new DefaultSecretStore(options) - return store + const store = new DefaultSecretStore(options) + return store } diff --git a/packages/core/src/DefaultStateStore/DefaultStateStore.impl.ts b/packages/core/src/DefaultStateStore/DefaultStateStore.impl.ts index f43add4e3..d38d6e5ce 100644 --- a/packages/core/src/DefaultStateStore/DefaultStateStore.impl.ts +++ b/packages/core/src/DefaultStateStore/DefaultStateStore.impl.ts @@ -11,32 +11,32 @@ import type { DefaultStateStoreConfig } from './types/index.js' * */ export class DefaultStateStore extends StateStoreBaseClass implements StateStore { - private map = new Map() - constructor(config?: StoreBaseConfig) { - super('DefaultStateStore', { ...config }) - if (config?.config) { - this.map = new Map(Object.entries(config.config)) - } - this.logger.warn( - 'Using the DefaultStateStore is not secure! It should only be used for test or development purpose.', - ) - } + private map = new Map() + constructor(config?: StoreBaseConfig) { + super('DefaultStateStore', { ...config }) + if (config?.config) { + this.map = new Map(Object.entries(config.config)) + } + this.logger.warn( + 'Using the DefaultStateStore is not secure! It should only be used for test or development purpose.', + ) + } - protected async getStateImpl( - ...stateNames: StateNames - ): Promise> { - const result: Record = {} - stateNames.forEach((name) => { - result[name] = this.map.get(name) - }) - return result as ObjectWithKeysFromStringArray - } + protected async getStateImpl( + ...stateNames: StateNames + ): Promise> { + const result: Record = {} + for (const name of stateNames) { + result[name] = this.map.get(name) + } + return result as ObjectWithKeysFromStringArray + } - protected async setStateImpl(stateName: string, stateValue: unknown) { - this.map.set(stateName, stateValue) - } + protected async setStateImpl(stateName: string, stateValue: unknown) { + this.map.set(stateName, stateValue) + } - protected async removeStateImpl(stateName: string) { - this.map.delete(stateName) - } + protected async removeStateImpl(stateName: string) { + this.map.delete(stateName) + } } diff --git a/packages/core/src/DefaultStateStore/defaultStateStore.test.ts b/packages/core/src/DefaultStateStore/defaultStateStore.test.ts index 77d4d1aad..913701f00 100644 --- a/packages/core/src/DefaultStateStore/defaultStateStore.test.ts +++ b/packages/core/src/DefaultStateStore/defaultStateStore.test.ts @@ -4,61 +4,61 @@ import { getLoggerMock } from '../mocks/index.js' import { DefaultStateStore } from './DefaultStateStore.impl.js' describe('DefaultStateStore', () => { - const sandbox = createSandbox() + const sandbox = createSandbox() - afterEach(() => { - sandbox.restore() - }) + afterEach(() => { + sandbox.restore() + }) - it('throws if operation is disabled', async () => { - const logger = getLoggerMock(sandbox) - const store = new DefaultStateStore({ - logger: logger.mock, - enableGet: false, - enableRemove: false, - enableSet: false, - }) + it('throws if operation is disabled', async () => { + const logger = getLoggerMock(sandbox) + const store = new DefaultStateStore({ + logger: logger.mock, + enableGet: false, + enableRemove: false, + enableSet: false, + }) - await expect(store.getState('example')).rejects.toThrow('get state from store is disabled by config') + await expect(store.getState('example')).rejects.toThrow('get state from store is disabled by config') - await expect(store.removeState('example')).rejects.toThrow('remove state from store is disabled by config') + await expect(store.removeState('example')).rejects.toThrow('remove state from store is disabled by config') - await expect(store.setState('example', 'value')).rejects.toThrow('set state at store is disabled by config') + await expect(store.setState('example', 'value')).rejects.toThrow('set state at store is disabled by config') - expect( - logger.stubs.warn.calledWith( - 'Using the DefaultStateStore is not secure! It should only be used for test or development purpose.', - ), - ).toBeTruthy() - }) + expect( + logger.stubs.warn.calledWith( + 'Using the DefaultStateStore is not secure! It should only be used for test or development purpose.', + ), + ).toBeTruthy() + }) - it('handles configs', async () => { - const logger = getLoggerMock(sandbox) - const store = new DefaultStateStore({ - logger: logger.mock, - enableGet: true, - enableRemove: true, - enableSet: true, - config: { - initialValue: 'initial', - }, - }) + it('handles configs', async () => { + const logger = getLoggerMock(sandbox) + const store = new DefaultStateStore({ + logger: logger.mock, + enableGet: true, + enableRemove: true, + enableSet: true, + config: { + initialValue: 'initial', + }, + }) - await expect(store.getState('initialValue', 'unknownState')).resolves.toEqual({ - initialValue: 'initial', - unknownState: undefined, - }) + await expect(store.getState('initialValue', 'unknownState')).resolves.toEqual({ + initialValue: 'initial', + unknownState: undefined, + }) - await expect(store.setState('initialValue', 'other_value')).resolves.toBeUndefined() + await expect(store.setState('initialValue', 'other_value')).resolves.toBeUndefined() - await expect(store.getState('initialValue')).resolves.toEqual({ - initialValue: 'other_value', - }) + await expect(store.getState('initialValue')).resolves.toEqual({ + initialValue: 'other_value', + }) - await expect(store.removeState('initialValue')).resolves.toBeUndefined() + await expect(store.removeState('initialValue')).resolves.toBeUndefined() - await expect(store.getState('initialValue')).resolves.toEqual({ - initialValue: undefined, - }) - }) + await expect(store.getState('initialValue')).resolves.toEqual({ + initialValue: undefined, + }) + }) }) diff --git a/packages/core/src/DefaultStateStore/initDefaultStateStore.impl.ts b/packages/core/src/DefaultStateStore/initDefaultStateStore.impl.ts index deb4a2d8e..56917ae33 100644 --- a/packages/core/src/DefaultStateStore/initDefaultStateStore.impl.ts +++ b/packages/core/src/DefaultStateStore/initDefaultStateStore.impl.ts @@ -2,6 +2,6 @@ import type { Logger } from '../core/index.js' import { DefaultStateStore } from './DefaultStateStore.impl.js' export const initDefaultStateStore = (options: { logger: Logger }): DefaultStateStore => { - const store = new DefaultStateStore(options) - return store + const store = new DefaultStateStore(options) + return store } diff --git a/packages/core/src/HttpClient/HttpClient.impl.ts b/packages/core/src/HttpClient/HttpClient.impl.ts index a4829c657..2dc1e5a8b 100644 --- a/packages/core/src/HttpClient/HttpClient.impl.ts +++ b/packages/core/src/HttpClient/HttpClient.impl.ts @@ -1,14 +1,16 @@ import { join } from 'node:path' import type { Context, Span, SpanOptions } from '@opentelemetry/api' -import { context, propagation, SpanKind, SpanStatusCode } from '@opentelemetry/api' +import { SpanKind, SpanStatusCode, context, propagation } from '@opentelemetry/api' import { Resource } from '@opentelemetry/resources' import type { SpanProcessor } from '@opentelemetry/sdk-trace-node' import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node' -import { SemanticAttributes, SemanticResourceAttributes } from '@opentelemetry/semantic-conventions' +import { ATTR_SERVICE_NAME } from '@opentelemetry/semantic-conventions' -import type { Logger } from '../core/index.js' -import { HandledError, initLogger, PuristaSpanTag, StatusCode, UnhandledError } from '../core/index.js' +import { ATTR_HTTP_METHOD, ATTR_HTTP_STATUS_CODE, ATTR_URL_FULL } from '@opentelemetry/semantic-conventions/incubating' + +import type { EmptyObject, Logger } from '../core/index.js' +import { HandledError, PuristaSpanTag, StatusCode, UnhandledError, initLogger } from '../core/index.js' import { puristaVersion } from '../version.js' import type { AuthCredentials, HttpClientConfig, HttpClientRequestOptions, RestClient } from './types/index.js' @@ -25,298 +27,298 @@ import type { AuthCredentials, HttpClientConfig, HttpClientRequestOptions, RestC * const result = await client.get('v1/orders') * ``` */ -export class HttpClient = {}> implements RestClient { - public name = 'HttpClient' - public logger: Logger - public config: HttpClientConfig - - public timeout: number - - public baseUrl: URL - - spanProcessor: SpanProcessor | undefined - traceProvider: NodeTracerProvider - - protected auth: AuthCredentials - constructor(config: HttpClientConfig) { - const name = config.name ?? this.name - this.name = name - - const logger = config.logger?.getChildLogger({ name }) ?? initLogger(config.logLevel, { name }) - - this.config = { - logger, - isKeepAlive: true, - defaultTimeout: 30000, - ...config, - } - - this.baseUrl = new URL(this.config.baseUrl) - - this.auth = { - basicAuth: this.config.basicAuth, - bearerToken: this.config.bearerToken, - } - this.timeout = this.config.defaultTimeout ?? 30000 - this.logger = logger - - const resource = Resource.default().merge( - new Resource({ - [SemanticResourceAttributes.SERVICE_NAME]: this.name, - }), - ) - this.traceProvider = new NodeTracerProvider({ - resource, - }) - - if (config.spanProcessor) { - this.traceProvider.addSpanProcessor(config.spanProcessor) - } - - this.traceProvider.register() - } - - /** - * Returns open telemetry tracer of this service - * - * @returns Tracer - */ - getTracer() { - return this.traceProvider.getTracer(this.name) - } - - /** - * Start a child span for opentelemetry tracking - * @param name name of span - * @param opts span options - * @param context optional context - * @param fn function to be executed within the span - * @returns return value of fn - */ - async startActiveSpan( - name: string, - opts: SpanOptions, - context: Context | undefined, - fn: (span: Span) => Promise, - ): Promise { - const tracer = this.getTracer() - - const callback = async (span: Span) => { - span.setAttribute(PuristaSpanTag.PuristaVersion, puristaVersion) - try { - return await fn(span) - } catch (error) { - let message = 'error' - if (error instanceof Error) { - message = error.message - } - - span.recordException(error as Error) - span.setStatus({ - code: SpanStatusCode.ERROR, - message, - }) - - throw error - } finally { - span.end() - } - } - - return context - ? tracer.startActiveSpan(name, opts, context, callback) - : tracer.startActiveSpan(name, opts, callback) - } - - protected getUrlAndHeader(path: string, options?: HttpClientRequestOptions) { - let fullPath = join(this.baseUrl.pathname, path) - - if (options?.hash) { - fullPath += `#${options.hash}` - } - - const url = new URL(fullPath, this.baseUrl) - - for (const [key, value] of Object.entries(options?.query ?? {})) { - url.searchParams.set(key, value) - } - - if (this.auth.basicAuth) { - url.password = this.auth.basicAuth.password - url.username = this.auth.basicAuth.username - } - - const headers: Record = { - ...this.config.defaultHeaders, - ...options?.headers, - } - - propagation.inject(context.active(), headers) - - if (this.auth.bearerToken) { - headers['Authorization'] = `Bearer ${this.auth.bearerToken}` - } - - return { - url, - headers, - } - } - - /** - * Set the bearer token for all following requests. - * @param token the bearer token - */ - setBearerToken(token: string | undefined) { - this.auth.bearerToken = token - } - - /** - * Helper method - * @param method - * @param path - * @param options - * @param payload - * @throws UnhandledError - * @returns - */ - protected async execute(method: string, path: string, options?: HttpClientRequestOptions, payload?: unknown) { - const controller = new AbortController() - const timeout = setTimeout(() => { - controller.abort( - new UnhandledError(StatusCode.RequestTimeout, `request exceeded ${this.timeout} ms`, { - name: this.name, - path, - method, - }), - ) - }, this.timeout) - - let body: string | undefined - - if (typeof payload === 'string') { - body = payload - } else { - body = payload ? JSON.stringify(payload) : undefined - } - - return this.startActiveSpan(`${this.name}.${method}`, { kind: SpanKind.CLIENT }, context.active(), async (span) => { - span.setAttribute(SemanticAttributes.HTTP_METHOD, method) - - const log = this.logger.getChildLogger({ ...span.spanContext(), customTraceId: this.config.traceId }) - - try { - const { url, headers } = this.getUrlAndHeader(path, options) - span.setAttribute(SemanticAttributes.HTTP_URL, url.toString()) - - const response = await fetch(url, { - method, - signal: controller.signal, - keepalive: this.config.isKeepAlive, - headers, - credentials: 'include', - body, - }) - - span.setAttribute(SemanticAttributes.HTTP_STATUS_CODE, response.status) - - if (!response.ok) { - let body = '' - try { - if (response.headers.get('content-type')?.startsWith('application/json')) { - body = await response.json() - } else { - body = await response.text() - } - } catch (err) { - log.warn({ err, method, url, path }, 'unable to get response text') - } - - const headers = Array.from(response.headers) - - const err = new UnhandledError(response.status as StatusCode, response.statusText, { - statusCode: response.status, - method, - url, - path, - headers, - response: body, - }) - throw err - } - - if (response.status === StatusCode.NoContent) { - return undefined - } - - if (response.headers.get('content-type')?.startsWith('application/json')) { - return await response.json() - } - return response.text() - } catch (error) { - const err = - error instanceof UnhandledError || error instanceof HandledError ? error : UnhandledError.fromError(error) - - log.error({ err, method, path }, err.message) - span.recordException(err) - span.setStatus({ - code: SpanStatusCode.ERROR, - message: err.message, - }) - throw err - } finally { - clearTimeout(timeout) - } - }) - } - - /** - * GET request - * @param path - * @param options - * @returns - */ - async get(path: string, options?: HttpClientRequestOptions): Promise { - return this.execute('GET', path, options) as Promise - } - - /** - * POST request - * @param path - * @param options - * @returns - */ - async post(path: string, payload: unknown, options?: HttpClientRequestOptions): Promise { - return this.execute('POST', path, options, payload) as Promise - } - - /** - * PUT request - * @param path - * @param options - * @returns - */ - async put(path: string, payload: unknown, options?: HttpClientRequestOptions): Promise { - return this.execute('PUT', path, options, payload) as Promise - } - - /** - * PATCH request - * @param path - * @param options - * @returns - */ - async patch(path: string, payload: unknown, options?: HttpClientRequestOptions): Promise { - return this.execute('PATCH', path, options, payload) as Promise - } - - /** - * DELETE request - * @param path - * @param options - * @returns - */ - async delete(path: string, options?: HttpClientRequestOptions, payload?: unknown): Promise { - return this.execute('DELETE', path, options, payload) as Promise - } +export class HttpClient = EmptyObject> implements RestClient { + public name = 'HttpClient' + public logger: Logger + public config: HttpClientConfig + + public timeout: number + + public baseUrl: URL + + spanProcessor: SpanProcessor | undefined + traceProvider: NodeTracerProvider + + protected auth: AuthCredentials + constructor(config: HttpClientConfig) { + const name = config.name ?? this.name + this.name = name + + const logger = config.logger?.getChildLogger({ name }) ?? initLogger(config.logLevel, { name }) + + this.config = { + logger, + isKeepAlive: true, + defaultTimeout: 30000, + ...config, + } + + this.baseUrl = new URL(this.config.baseUrl) + + this.auth = { + basicAuth: this.config.basicAuth, + bearerToken: this.config.bearerToken, + } + this.timeout = this.config.defaultTimeout ?? 30000 + this.logger = logger + + const resource = Resource.default().merge( + new Resource({ + [ATTR_SERVICE_NAME]: this.name, + }), + ) + this.traceProvider = new NodeTracerProvider({ + resource, + }) + + if (config.spanProcessor) { + this.traceProvider.addSpanProcessor(config.spanProcessor) + } + + this.traceProvider.register() + } + + /** + * Returns open telemetry tracer of this service + * + * @returns Tracer + */ + getTracer() { + return this.traceProvider.getTracer(this.name) + } + + /** + * Start a child span for opentelemetry tracking + * @param name name of span + * @param opts span options + * @param context optional context + * @param fn function to be executed within the span + * @returns return value of fn + */ + async startActiveSpan( + name: string, + opts: SpanOptions, + context: Context | undefined, + fn: (span: Span) => Promise, + ): Promise { + const tracer = this.getTracer() + + const callback = async (span: Span) => { + span.setAttribute(PuristaSpanTag.PuristaVersion, puristaVersion) + try { + return await fn(span) + } catch (error) { + let message = 'error' + if (error instanceof Error) { + message = error.message + } + + span.recordException(error as Error) + span.setStatus({ + code: SpanStatusCode.ERROR, + message, + }) + + throw error + } finally { + span.end() + } + } + + return context + ? tracer.startActiveSpan(name, opts, context, callback) + : tracer.startActiveSpan(name, opts, callback) + } + + protected getUrlAndHeader(path: string, options?: HttpClientRequestOptions) { + let fullPath = join(this.baseUrl.pathname, path) + + if (options?.hash) { + fullPath += `#${options.hash}` + } + + const url = new URL(fullPath, this.baseUrl) + + for (const [key, value] of Object.entries(options?.query ?? {})) { + url.searchParams.set(key, value) + } + + if (this.auth.basicAuth) { + url.password = this.auth.basicAuth.password + url.username = this.auth.basicAuth.username + } + + const headers: Record = { + ...this.config.defaultHeaders, + ...options?.headers, + } + + propagation.inject(context.active(), headers) + + if (this.auth.bearerToken) { + headers.Authorization = `Bearer ${this.auth.bearerToken}` + } + + return { + url, + headers, + } + } + + /** + * Set the bearer token for all following requests. + * @param token the bearer token + */ + setBearerToken(token: string | undefined) { + this.auth.bearerToken = token + } + + /** + * Helper method + * @param method + * @param path + * @param options + * @param payload + * @throws UnhandledError + * @returns + */ + protected async execute(method: string, path: string, options?: HttpClientRequestOptions, payload?: unknown) { + const controller = new AbortController() + const timeout = setTimeout(() => { + controller.abort( + new UnhandledError(StatusCode.RequestTimeout, `request exceeded ${this.timeout} ms`, { + name: this.name, + path, + method, + }), + ) + }, this.timeout) + + let body: string | undefined + + if (typeof payload === 'string') { + body = payload + } else { + body = payload ? JSON.stringify(payload) : undefined + } + + return this.startActiveSpan(`${this.name}.${method}`, { kind: SpanKind.CLIENT }, context.active(), async span => { + span.setAttribute(ATTR_HTTP_METHOD, method) + + const log = this.logger.getChildLogger({ ...span.spanContext(), customTraceId: this.config.traceId }) + + try { + const { url, headers } = this.getUrlAndHeader(path, options) + span.setAttribute(ATTR_URL_FULL, url.toString()) + + const response = await fetch(url, { + method, + signal: controller.signal, + keepalive: this.config.isKeepAlive, + headers, + credentials: 'include', + body, + }) + + span.setAttribute(ATTR_HTTP_STATUS_CODE, response.status) + + if (!response.ok) { + let body = '' + try { + if (response.headers.get('content-type')?.startsWith('application/json')) { + body = await response.json() + } else { + body = await response.text() + } + } catch (err) { + log.warn({ err, method, url, path }, 'unable to get response text') + } + + const headers = Array.from(response.headers) + + const err = new UnhandledError(response.status as StatusCode, response.statusText, { + statusCode: response.status, + method, + url, + path, + headers, + response: body, + }) + throw err + } + + if (response.status === StatusCode.NoContent) { + return undefined + } + + if (response.headers.get('content-type')?.startsWith('application/json')) { + return await response.json() + } + return response.text() + } catch (error) { + const err = + error instanceof UnhandledError || error instanceof HandledError ? error : UnhandledError.fromError(error) + + log.error({ err, method, path }, err.message) + span.recordException(err) + span.setStatus({ + code: SpanStatusCode.ERROR, + message: err.message, + }) + throw err + } finally { + clearTimeout(timeout) + } + }) + } + + /** + * GET request + * @param path + * @param options + * @returns + */ + async get(path: string, options?: HttpClientRequestOptions): Promise { + return this.execute('GET', path, options) as Promise + } + + /** + * POST request + * @param path + * @param options + * @returns + */ + async post(path: string, payload: unknown, options?: HttpClientRequestOptions): Promise { + return this.execute('POST', path, options, payload) as Promise + } + + /** + * PUT request + * @param path + * @param options + * @returns + */ + async put(path: string, payload: unknown, options?: HttpClientRequestOptions): Promise { + return this.execute('PUT', path, options, payload) as Promise + } + + /** + * PATCH request + * @param path + * @param options + * @returns + */ + async patch(path: string, payload: unknown, options?: HttpClientRequestOptions): Promise { + return this.execute('PATCH', path, options, payload) as Promise + } + + /** + * DELETE request + * @param path + * @param options + * @returns + */ + async delete(path: string, options?: HttpClientRequestOptions, payload?: unknown): Promise { + return this.execute('DELETE', path, options, payload) as Promise + } } diff --git a/packages/core/src/HttpClient/httpClient.test.ts b/packages/core/src/HttpClient/httpClient.test.ts index 4d12a199f..341231b15 100644 --- a/packages/core/src/HttpClient/httpClient.test.ts +++ b/packages/core/src/HttpClient/httpClient.test.ts @@ -4,187 +4,187 @@ import { getLoggerMock } from '../mocks/index.js' import { HttpClient } from './HttpClient.impl.js' describe('HttpClient', () => { - const sandbox = createSandbox() - - afterEach(() => { - sandbox.restore() - sandbox.reset() - }) - - it('can post', async () => { - const logger = getLoggerMock() - const client = new HttpClient({ - baseUrl: 'http://example.com', - logger: logger.mock, - defaultHeaders: { - 'content-type': 'application/json; charset=utf-8', - }, - }) - - client.setBearerToken('123') - - const payload = { some: 'data' } - const response = { ok: 'ok' } - - sandbox.stub(global, 'fetch').callsFake((url, req) => { - expect(url).toStrictEqual(new URL('http://example.com/example')) - expect(req?.method).toBe('POST') - - const x = req?.headers as any - expect(x['Authorization']).toBe('Bearer 123') - expect(x['content-type']).toBe('application/json; charset=utf-8') - - return Promise.resolve({ - headers: { - get: () => 'application/json', - }, - ok: true, - json: () => Promise.resolve(response), - text: () => Promise.resolve(JSON.stringify(response)), - } as any) - }) - - await expect(client.post('/example', payload)).resolves.toBe(response) - }) - - it('can patch', async () => { - const logger = getLoggerMock() - const client = new HttpClient({ baseUrl: 'http://example.com', logger: logger.mock }) - - const payload = { some: 'data' } - const response = { ok: 'ok' } - - sandbox.stub(global, 'fetch').callsFake((url, req) => { - expect(url).toStrictEqual(new URL('http://example.com/example')) - expect(req?.method).toBe('PATCH') - expect(req?.body).toBe(JSON.stringify(payload)) - return Promise.resolve({ - headers: { - get: () => 'application/json', - }, - ok: true, - json: () => Promise.resolve(response), - text: () => Promise.resolve(JSON.stringify(response)), - } as any) - }) - - await expect(client.patch('/example', payload)).resolves.toBe(response) - }) - - it('can put', async () => { - const logger = getLoggerMock() - const client = new HttpClient({ baseUrl: 'http://example.com', logger: logger.mock }) - - const payload = { some: 'data' } - const response = { ok: 'ok' } - - sandbox.stub(global, 'fetch').callsFake((url, req) => { - expect(url).toStrictEqual(new URL('http://example.com/example')) - expect(req?.method).toBe('PUT') - expect(req?.body).toBe(JSON.stringify(payload)) - return Promise.resolve({ - headers: { - get: () => 'application/json', - }, - ok: true, - json: () => Promise.resolve(response), - text: () => Promise.resolve(JSON.stringify(response)), - } as any) - }) - - await expect(client.put('/example', payload)).resolves.toBe(response) - }) - - it('can delete', async () => { - const logger = getLoggerMock() - const client = new HttpClient({ baseUrl: 'http://example.com', logger: logger.mock }) - - const response = { ok: 'ok' } - - sandbox.stub(global, 'fetch').callsFake((url, req) => { - expect(url).toStrictEqual(new URL('http://example.com/example')) - expect(req?.method).toBe('DELETE') - expect(req?.body).toBeUndefined() - return Promise.resolve({ - headers: { - get: () => 'application/json', - }, - ok: true, - json: () => Promise.resolve(response), - text: () => Promise.resolve(JSON.stringify(response)), - } as any) - }) - - await expect(client.delete('/example')).resolves.toBe(response) - }) - - it('can get', async () => { - const logger = getLoggerMock() - const client = new HttpClient({ baseUrl: 'http://example.com', logger: logger.mock }) - - const response = { ok: 'ok' } - - sandbox.stub(global, 'fetch').callsFake((url, req) => { - expect(url).toStrictEqual(new URL('http://example.com/example')) - expect(req?.method).toBe('GET') - expect(req?.body).toBeUndefined() - return Promise.resolve({ - headers: { - get: () => 'application/json', - }, - ok: true, - json: () => Promise.resolve(response), - text: () => Promise.resolve(JSON.stringify(response)), - } as any) - }) - - await expect(client.get('/example')).resolves.toBe(response) - }) - - it('can get json', async () => { - const logger = getLoggerMock() - const client = new HttpClient({ baseUrl: 'http://example.com', logger: logger.mock }) - - const response = { ok: 'ok' } - - sandbox.stub(global, 'fetch').callsFake((url, req) => { - expect(url).toStrictEqual(new URL('http://example.com/example')) - expect(req?.method).toBe('GET') - expect(req?.body).toBeUndefined() - return Promise.resolve({ - headers: { - get: () => 'application/json', - }, - ok: true, - json: () => Promise.resolve(response), - text: () => Promise.resolve(JSON.stringify(response)), - } as any) - }) - - await expect(client.get('/example')).resolves.toBe(response) - }) - - it('throws', async () => { - const logger = getLoggerMock() - const client = new HttpClient({ baseUrl: 'http://example.com', logger: logger.mock }) - - const response = { ok: 'ok' } - - sandbox.stub(global, 'fetch').callsFake((url, req) => { - expect(url).toStrictEqual(new URL('http://example.com/example')) - expect(req?.method).toBe('GET') - expect(req?.body).toBeUndefined() - return Promise.resolve({ - status: 400, - headers: { - get: () => 'application/json', - }, - ok: false, - json: () => Promise.resolve(response), - text: () => Promise.resolve(JSON.stringify(response)), - } as any) - }) - - await expect(client.get('/example')).rejects.toThrow('Bad Request') - }) + const sandbox = createSandbox() + + afterEach(() => { + sandbox.restore() + sandbox.reset() + }) + + it('can post', async () => { + const logger = getLoggerMock() + const client = new HttpClient({ + baseUrl: 'http://example.com', + logger: logger.mock, + defaultHeaders: { + 'content-type': 'application/json; charset=utf-8', + }, + }) + + client.setBearerToken('123') + + const payload = { some: 'data' } + const response = { ok: 'ok' } + + sandbox.stub(global, 'fetch').callsFake((url, req) => { + expect(url).toStrictEqual(new URL('http://example.com/example')) + expect(req?.method).toBe('POST') + + const x = req?.headers as any + expect(x.Authorization).toBe('Bearer 123') + expect(x['content-type']).toBe('application/json; charset=utf-8') + + return Promise.resolve({ + headers: { + get: () => 'application/json', + }, + ok: true, + json: () => Promise.resolve(response), + text: () => Promise.resolve(JSON.stringify(response)), + } as any) + }) + + await expect(client.post('/example', payload)).resolves.toBe(response) + }) + + it('can patch', async () => { + const logger = getLoggerMock() + const client = new HttpClient({ baseUrl: 'http://example.com', logger: logger.mock }) + + const payload = { some: 'data' } + const response = { ok: 'ok' } + + sandbox.stub(global, 'fetch').callsFake((url, req) => { + expect(url).toStrictEqual(new URL('http://example.com/example')) + expect(req?.method).toBe('PATCH') + expect(req?.body).toBe(JSON.stringify(payload)) + return Promise.resolve({ + headers: { + get: () => 'application/json', + }, + ok: true, + json: () => Promise.resolve(response), + text: () => Promise.resolve(JSON.stringify(response)), + } as any) + }) + + await expect(client.patch('/example', payload)).resolves.toBe(response) + }) + + it('can put', async () => { + const logger = getLoggerMock() + const client = new HttpClient({ baseUrl: 'http://example.com', logger: logger.mock }) + + const payload = { some: 'data' } + const response = { ok: 'ok' } + + sandbox.stub(global, 'fetch').callsFake((url, req) => { + expect(url).toStrictEqual(new URL('http://example.com/example')) + expect(req?.method).toBe('PUT') + expect(req?.body).toBe(JSON.stringify(payload)) + return Promise.resolve({ + headers: { + get: () => 'application/json', + }, + ok: true, + json: () => Promise.resolve(response), + text: () => Promise.resolve(JSON.stringify(response)), + } as any) + }) + + await expect(client.put('/example', payload)).resolves.toBe(response) + }) + + it('can delete', async () => { + const logger = getLoggerMock() + const client = new HttpClient({ baseUrl: 'http://example.com', logger: logger.mock }) + + const response = { ok: 'ok' } + + sandbox.stub(global, 'fetch').callsFake((url, req) => { + expect(url).toStrictEqual(new URL('http://example.com/example')) + expect(req?.method).toBe('DELETE') + expect(req?.body).toBeUndefined() + return Promise.resolve({ + headers: { + get: () => 'application/json', + }, + ok: true, + json: () => Promise.resolve(response), + text: () => Promise.resolve(JSON.stringify(response)), + } as any) + }) + + await expect(client.delete('/example')).resolves.toBe(response) + }) + + it('can get', async () => { + const logger = getLoggerMock() + const client = new HttpClient({ baseUrl: 'http://example.com', logger: logger.mock }) + + const response = { ok: 'ok' } + + sandbox.stub(global, 'fetch').callsFake((url, req) => { + expect(url).toStrictEqual(new URL('http://example.com/example')) + expect(req?.method).toBe('GET') + expect(req?.body).toBeUndefined() + return Promise.resolve({ + headers: { + get: () => 'application/json', + }, + ok: true, + json: () => Promise.resolve(response), + text: () => Promise.resolve(JSON.stringify(response)), + } as any) + }) + + await expect(client.get('/example')).resolves.toBe(response) + }) + + it('can get json', async () => { + const logger = getLoggerMock() + const client = new HttpClient({ baseUrl: 'http://example.com', logger: logger.mock }) + + const response = { ok: 'ok' } + + sandbox.stub(global, 'fetch').callsFake((url, req) => { + expect(url).toStrictEqual(new URL('http://example.com/example')) + expect(req?.method).toBe('GET') + expect(req?.body).toBeUndefined() + return Promise.resolve({ + headers: { + get: () => 'application/json', + }, + ok: true, + json: () => Promise.resolve(response), + text: () => Promise.resolve(JSON.stringify(response)), + } as any) + }) + + await expect(client.get('/example')).resolves.toBe(response) + }) + + it('throws', async () => { + const logger = getLoggerMock() + const client = new HttpClient({ baseUrl: 'http://example.com', logger: logger.mock }) + + const response = { ok: 'ok' } + + sandbox.stub(global, 'fetch').callsFake((url, req) => { + expect(url).toStrictEqual(new URL('http://example.com/example')) + expect(req?.method).toBe('GET') + expect(req?.body).toBeUndefined() + return Promise.resolve({ + status: 400, + headers: { + get: () => 'application/json', + }, + ok: false, + json: () => Promise.resolve(response), + text: () => Promise.resolve(JSON.stringify(response)), + } as any) + }) + + await expect(client.get('/example')).rejects.toThrow('Bad Request') + }) }) diff --git a/packages/core/src/HttpClient/types/AuthCredentials.ts b/packages/core/src/HttpClient/types/AuthCredentials.ts index 4e964ccdf..664b212ef 100644 --- a/packages/core/src/HttpClient/types/AuthCredentials.ts +++ b/packages/core/src/HttpClient/types/AuthCredentials.ts @@ -2,13 +2,13 @@ * HTTP authentication information */ export type AuthCredentials = { - /** Basic-Auth information */ - basicAuth?: { - /** Basic-Auth username */ - username: string - /** Basic-Auth password */ - password: string - } - /** Bearer token header */ - bearerToken?: string + /** Basic-Auth information */ + basicAuth?: { + /** Basic-Auth username */ + username: string + /** Basic-Auth password */ + password: string + } + /** Bearer token header */ + bearerToken?: string } diff --git a/packages/core/src/HttpClient/types/HttpClientConfig.ts b/packages/core/src/HttpClient/types/HttpClientConfig.ts index 8f46d7b85..fd8d3c4e9 100644 --- a/packages/core/src/HttpClient/types/HttpClientConfig.ts +++ b/packages/core/src/HttpClient/types/HttpClientConfig.ts @@ -1,75 +1,75 @@ import type { SpanProcessor } from '@opentelemetry/sdk-trace-node' -import type { Logger, LogLevelName, Prettify } from '../../core/index.js' +import type { LogLevelName, Logger, Prettify } from '../../core/index.js' /** * Tha basic configuration for a HTTPClient * Requires as least a `baseUrl` */ export type HttpClientConfig> = Prettify< - { - /** - * the base url to be used - * @example - * ```typescript - * const config = { - * baseUrl: 'http://localhost/api` - * } - * // each request will be below http://localhost/api - * // get('v1/orders') will call http://localhost/api/v1/orders - * ``` - * */ - baseUrl: string - /** - * A logger instance - */ - logger?: Logger - /** - * the loglevel if no logger instance is given - */ - logLevel?: LogLevelName - /** - * If set to false, the HTTP client will not reuse the same connection for multiple requests. - * Default is true. - */ - isKeepAlive?: boolean - /** - * Name of the client - */ - name?: string - /** - * Add your default headers here - * These headers will be part of every request. - * They can be overwritten per request option - * */ - defaultHeaders?: Record - /** - * set global timeout for requests in ms - * @default 30000 - */ - defaultTimeout?: number - /** - * Basic-Auth information - */ - basicAuth?: { - /** Basic-Auth username */ - username: string - /** Basic-Auth password */ - password: string - } - /** Auth-Bearer token */ - bearerToken?: string + { + /** + * the base url to be used + * @example + * ```typescript + * const config = { + * baseUrl: 'http://localhost/api` + * } + * // each request will be below http://localhost/api + * // get('v1/orders') will call http://localhost/api/v1/orders + * ``` + * */ + baseUrl: string + /** + * A logger instance + */ + logger?: Logger + /** + * the loglevel if no logger instance is given + */ + logLevel?: LogLevelName + /** + * If set to false, the HTTP client will not reuse the same connection for multiple requests. + * Default is true. + */ + isKeepAlive?: boolean + /** + * Name of the client + */ + name?: string + /** + * Add your default headers here + * These headers will be part of every request. + * They can be overwritten per request option + * */ + defaultHeaders?: Record + /** + * set global timeout for requests in ms + * @default 30000 + */ + defaultTimeout?: number + /** + * Basic-Auth information + */ + basicAuth?: { + /** Basic-Auth username */ + username: string + /** Basic-Auth password */ + password: string + } + /** Auth-Bearer token */ + bearerToken?: string - /** - * Opentelemetry span processor - */ - spanProcessor?: SpanProcessor - /** - * enable Opentelemetry tracing. - * The client will be handled as own ressource. - */ - enableOpentelemetry?: boolean - /** Custom trace Id */ - traceId?: string - } & CustomConfig + /** + * Opentelemetry span processor + */ + spanProcessor?: SpanProcessor + /** + * enable Opentelemetry tracing. + * The client will be handled as own resource. + */ + enableOpentelemetry?: boolean + /** Custom trace Id */ + traceId?: string + } & CustomConfig > diff --git a/packages/core/src/HttpClient/types/HttpClientRequestOptions.ts b/packages/core/src/HttpClient/types/HttpClientRequestOptions.ts index b1db4b0e4..c6cccfe31 100644 --- a/packages/core/src/HttpClient/types/HttpClientRequestOptions.ts +++ b/packages/core/src/HttpClient/types/HttpClientRequestOptions.ts @@ -2,22 +2,22 @@ * Options for a single request */ export type HttpClientRequestOptions = { - /** - * additional headers - */ - headers?: Record - /** - * query/search string parameter - */ - query?: Record - /** - * url hash - * @example: http://example.com/index.html#hash - */ - hash?: string - /** - * Timeout for the request in ms - * @default 30000 - */ - timeout?: number + /** + * additional headers + */ + headers?: Record + /** + * query/search string parameter + */ + query?: Record + /** + * url hash + * @example: http://example.com/index.html#hash + */ + hash?: string + /** + * Timeout for the request in ms + * @default 30000 + */ + timeout?: number } diff --git a/packages/core/src/HttpClient/types/RestClient.ts b/packages/core/src/HttpClient/types/RestClient.ts index cbf3f0dde..12210e39a 100644 --- a/packages/core/src/HttpClient/types/RestClient.ts +++ b/packages/core/src/HttpClient/types/RestClient.ts @@ -5,57 +5,57 @@ import type { HttpClientRequestOptions } from './HttpClientRequestOptions.js' * The client provides error and timeout handling and tries to decode the responses */ export interface RestClient { - /** - * Set the Auth-Bearer-Token for all following requests - * @param token the bearer token - */ - setBearerToken(token: string | undefined): void + /** + * Set the Auth-Bearer-Token for all following requests + * @param token the bearer token + */ + setBearerToken(token: string | undefined): void - /** - * Make a GET request against baseUrl+path - * Returns body text if response content type is not set to `application/json`. - * If response content type is `application/json`, the JSON parsed result will be returned - * @param path - * @param options - */ - get(path: string, options: HttpClientRequestOptions): Promise + /** + * Make a GET request against baseUrl+path + * Returns body text if response content type is not set to `application/json`. + * If response content type is `application/json`, the JSON parsed result will be returned + * @param path + * @param options + */ + get(path: string, options: HttpClientRequestOptions): Promise - /** - * Make a POST request against baseUrl+path - * Returns body text if response content type is not set to `application/json`. - * If response content type is `application/json`, the JSON parsed result will be returned - * @param path - * @param payload - * @param options - */ - post(path: string, payload: unknown, options: HttpClientRequestOptions): Promise + /** + * Make a POST request against baseUrl+path + * Returns body text if response content type is not set to `application/json`. + * If response content type is `application/json`, the JSON parsed result will be returned + * @param path + * @param payload + * @param options + */ + post(path: string, payload: unknown, options: HttpClientRequestOptions): Promise - /** - * Make a PUT request against baseUrl+path - * Returns body text if response content type is not set to `application/json`. - * If response content type is `application/json`, the JSON parsed result will be returned - * @param path - * @param payload - * @param options - */ - put(path: string, payload: unknown, options: HttpClientRequestOptions): Promise + /** + * Make a PUT request against baseUrl+path + * Returns body text if response content type is not set to `application/json`. + * If response content type is `application/json`, the JSON parsed result will be returned + * @param path + * @param payload + * @param options + */ + put(path: string, payload: unknown, options: HttpClientRequestOptions): Promise - /** - * Make a PATCH request against baseUrl+path - * Returns body text if response content type is not set to `application/json`. - * If response content type is `application/json`, the JSON parsed result will be returned - * @param path - * @param payload - * @param options - */ - patch(path: string, payload: unknown, options: HttpClientRequestOptions): Promise + /** + * Make a PATCH request against baseUrl+path + * Returns body text if response content type is not set to `application/json`. + * If response content type is `application/json`, the JSON parsed result will be returned + * @param path + * @param payload + * @param options + */ + patch(path: string, payload: unknown, options: HttpClientRequestOptions): Promise - /** - * Make a DELETE request against baseUrl+path - * Returns body text if response content type is not set to `application/json`. - * If response content type is `application/json`, the JSON parsed result will be returned - * @param path - * @param options - */ - delete(path: string, options: HttpClientRequestOptions): Promise + /** + * Make a DELETE request against baseUrl+path + * Returns body text if response content type is not set to `application/json`. + * If response content type is `application/json`, the JSON parsed result will be returned + * @param path + * @param options + */ + delete(path: string, options: HttpClientRequestOptions): Promise } diff --git a/packages/core/src/ServiceBuilder/ServiceBuilder.impl.ts b/packages/core/src/ServiceBuilder/ServiceBuilder.impl.ts index fd9bd7bca..4fbcd1ab3 100644 --- a/packages/core/src/ServiceBuilder/ServiceBuilder.impl.ts +++ b/packages/core/src/ServiceBuilder/ServiceBuilder.impl.ts @@ -1,33 +1,58 @@ import { fail } from 'node:assert' import type { SpanProcessor } from '@opentelemetry/sdk-trace-node' -import type { Infer, InferIn, Schema } from '@typeschema/main' +import { type Infer, type InferIn, type Schema, validate } from '@typeschema/main' +import type { CommandDefinitionBuilderTypes } from '../CommandDefinitionBuilder/CommandDefinitionBuilderTypes.js' import { CommandDefinitionBuilder } from '../CommandDefinitionBuilder/index.js' -import type { - CommandDefinitionList, - CommandDefinitionListResolved, - Complete, - ConfigStore, - EventBridge, - Logger, - LogLevelName, - SecretStore, - ServiceClass, - ServiceConstructorInput, - ServiceInfoType, - StateStore, - SubscriptionDefinitionList, - SubscriptionDefinitionListResolved, -} from '../core/index.js' -import { initLogger, Service, StatusCode, UnhandledError } from '../core/index.js' import { initDefaultConfigStore } from '../DefaultConfigStore/index.js' import { initDefaultSecretStore } from '../DefaultSecretStore/index.js' import { initDefaultStateStore } from '../DefaultStateStore/index.js' -import type { NonEmptyString } from '../helper/index.js' -import { SubscriptionDefinitionBuilder } from '../SubscriptionDefinitionBuilder/index.js' - -export type Newable = { new (config: ServiceConstructorInput): T } +import { + SubscriptionDefinitionBuilder, + type SubscriptionDefinitionBuilderTypes, +} from '../SubscriptionDefinitionBuilder/index.js' +import type { + CommandDefinitionList, + CommandDefinitionListResolved, + Complete, + ConfigStore, + EmptyObject, + EventBridge, + InvokeList, + LogLevelName, + Logger, + NeverObject, + Prettify, + SecretStore, + ServiceBuilderTypes, + ServiceClassTypes, + ServiceConstructorInput, + ServiceInfoType, + SetNewTypeValue, + SetNewTypeValues, + StateStore, + SubscriptionDefinitionList, + SubscriptionDefinitionListResolved, +} from '../core/index.js' +import { Service, StatusCode, UnhandledError, initLogger } from '../core/index.js' +import type { InstanceOrType, NonEmptyString } from '../helper/index.js' + +export type Newable = new (config: ServiceConstructorInput) => T + +type InstanceConfigType = Prettify< + { + logLevel?: LogLevelName + logger?: Logger + spanProcessor?: SpanProcessor + secretStore?: SecretStore + configStore?: ConfigStore + stateStore?: StateStore + } & (keyof S['Resources'] extends NeverObject ? { resources?: never } : { resources: S['Resources'] }) & + (keyof S['ConfigInputType'] extends NeverObject + ? { serviceConfig?: never } + : { serviceConfig: S['ConfigInputType'] }) +> /** * This class is used to build a service. @@ -37,308 +62,391 @@ export type Newable = { new (config: ServiceConstructorInput, - ConfigInputType = Record, - ServiceClassType extends ServiceClass = Service, -> { - private commandDefinitionList: CommandDefinitionList = [] - private subscriptionDefinitionList: SubscriptionDefinitionList = [] - - private commandDefinitionListResolved: CommandDefinitionListResolved = [] - private subscriptionDefinitionListResolved: SubscriptionDefinitionListResolved = [] - - private configSchema?: Schema - private defaultConfig?: Complete - - private definitionsResolved: boolean = false - - instance?: ServiceClassType - SClass: Newable = Service - - // eslint-disable-next-line no-useless-constructor - constructor(public info: ServiceInfoType) {} - - /** - * "This function sets the config schema for the service builder." - * - * @param schema - The schema that will be used to validate the config. - * @returns ServiceBuilder - */ - setConfigSchema(schema: T) { - this.configSchema = schema - return this as unknown as ServiceBuilder, InferIn, Service>> - } - - /** - * "This function sets the default configuration for the service." - * - * @param config - ConfigType - The default configuration for the service. - * @returns The ServiceBuilder instance. - */ - setDefaultConfig(config: Complete): ServiceBuilder { - this.defaultConfig = config - return this - } - - /** - * `addCommandDefinition` adds a list of command definitions to the service builder - * @param commands - CommandDefinitionList - * @returns The service builder - */ - addCommandDefinition(...commands: CommandDefinitionList) { - if (this.definitionsResolved) { - throw new UnhandledError( - StatusCode.InternalServerError, - 'You can not add commands after resolveDefinitions is called.', - ) - } - this.commandDefinitionList.push(...commands) - return this as ServiceBuilder - } - - /** - * It adds a subscription definition to the service builder - * @param subscription - SubscriptionDefinitionList - * @returns The service builder - */ - addSubscriptionDefinition(...subscription: SubscriptionDefinitionList) { - if (this.definitionsResolved) { - throw new UnhandledError( - StatusCode.InternalServerError, - 'You can not add subscriptions after resolveDefinitions is called.', - ) - } - this.subscriptionDefinitionList.push(...subscription) - return this as ServiceBuilder - } - - /** - * - * Resolves the command and subscription definitions - */ - public async resolveDefinitions() { - if (this.definitionsResolved) { - return { - commands: this.commandDefinitionListResolved, - subscriptions: this.subscriptionDefinitionListResolved, - } - } - - this.commandDefinitionListResolved = await Promise.all(this.commandDefinitionList) - this.subscriptionDefinitionListResolved = await Promise.all(this.subscriptionDefinitionList) - - this.subscriptionDefinitionList = [] - this.commandDefinitionList = [] - - this.definitionsResolved = true - return { - commands: this.commandDefinitionListResolved, - subscriptions: this.subscriptionDefinitionListResolved, - } - } - - /** - * It sets the class type of the service. - * @param customClass - A class which extends the Service class - * @returns The builder itself, but with the type of the service class changed. - */ - setCustomClass>(customClass: Newable) { - this.SClass = customClass as Newable - return this as unknown as ServiceBuilder - } - - getCustomClass() { - return this.SClass - } - - /** - * It creates a new instance of the service class, passing in the logger, service info, event bridge, - * command functions, subscription list, and configuration - * @param eventBridge - EventBridge - * @param options - additional config like logger, stores and opentelemetry span processor - * @returns The instance of the service class - */ - async getInstance( - eventBridge: EventBridge, - options: { - logLevel?: LogLevelName - serviceConfig?: Partial - logger?: Logger - spanProcessor?: SpanProcessor - secretStore?: SecretStore - configStore?: ConfigStore - stateStore?: StateStore - } = {}, - ) { - const config = { - ...this.defaultConfig, - ...options?.serviceConfig, - } as ConfigType - - const opt = options.serviceConfig as any - const hasLogLevel = opt?.logLevel - ? ['info', 'error', 'warn', 'debug', 'trace', 'fatal'].includes(opt.logLevel) - : false - - const logger = options.logger ?? initLogger(hasLogLevel ? opt.logLevel : options.logLevel) - - const secretStore: SecretStore = - options.secretStore ?? - initDefaultSecretStore({ - logger, - }) - - const configStore: ConfigStore = - options.configStore ?? - initDefaultConfigStore({ - logger, - }) - - const stateStore: StateStore = - options.stateStore ?? - initDefaultStateStore({ - logger, - }) - - const { commands, subscriptions } = await this.resolveDefinitions() - - const C = this.getCustomClass() - this.instance = new C({ - logger, - eventBridge, - info: this.info, - commandDefinitionList: commands, - subscriptionDefinitionList: subscriptions, - config, - spanProcessor: options.spanProcessor, - secretStore, - configStore, - stateStore, - configSchema: this.configSchema, - }) - - return this.instance as ServiceClassType - } - - /** - * It returns a new instance of the CommandDefinitionBuilder class, which is a class that is used to - * build a command definition - * @param commandName - The name of the command. - * @param description - The description of the command. - * @param eventName - The name of the event that will be emitted when the command is - * executed. - * @returns A CommandDefinitionBuilder object. - */ - getCommandBuilder( - commandName: NonEmptyString, - description: string, - eventName?: NonEmptyString, - ): CommandDefinitionBuilder { - return new CommandDefinitionBuilder(commandName, description, eventName) - } - - /** - * It returns a new instance of the `SubscriptionDefinitionBuilder` class, which is a class that is - * used to build a subscription definition - * @param subscriptionName - The name of the subscription. - * @param description - The description of the subscription. - * @returns A SubscriptionDefinitionBuilder - */ - getSubscriptionBuilder( - subscriptionName: NonEmptyString, - description: string, - ): SubscriptionDefinitionBuilder { - return new SubscriptionDefinitionBuilder(subscriptionName, description) - } - - /** - * @returns the definition of registered commands - */ - getCommandDefinitions() { - if (!this.resolveDefinitions) { - throw new UnhandledError( - StatusCode.InternalServerError, - 'Definitions not resolve. Please call resolveDefinitions() before using getCommandDefinitions', - ) - } - return this.commandDefinitionListResolved - } - - /** - * @returns the definition of registered subscriptions - */ - getSubscriptionDefinitions() { - if (!this.resolveDefinitions) { - throw new UnhandledError( - StatusCode.InternalServerError, - 'Definitions not resolve. Please call resolveDefinitions() before using getCommandDefinitions', - ) - } - return this.subscriptionDefinitionListResolved - } - - /** - * A simple test helper, which ensures, that there ar no duplicate names used. - */ - async testServiceSetup() { - const { subscriptions, commands } = await this.resolveDefinitions() - - this.validateCommands(commands) - this.validateSubscriptions(subscriptions) - - return true - } - - protected validateCommands(commandDefinitions: CommandDefinitionListResolved) { - const existingNames = new Set() - const eventNames = new Set() - - commandDefinitions.forEach((definition) => { - const name = definition.commandName.toLowerCase().trim() - const eventName = definition.eventName - - // check for duplicate command names - if (existingNames.has(name)) { - fail(`duplicate command name ${name}`) - } - existingNames.add(name) - - // check for duplicate event names - if (eventName) { - if (eventNames.has(eventName)) { - fail(`response event "${eventName}" in ${name} is used in other command`) - } - eventNames.add(eventName) - } - }) - } - - protected validateSubscriptions(subscriptionDefinitions: SubscriptionDefinitionListResolved) { - const existingNames = new Set() - subscriptionDefinitions.forEach((definition) => { - const name = definition.subscriptionName.toLowerCase().trim() - - if (existingNames.has(name)) { - fail(`duplicate subscription name ${name}`) - } - existingNames.add(name) - }) - } - - /** - * @deprecated Use validateServiceConfig() instead - */ - validateCommandDefinitions() { - // eslint-disable-next-line no-console - console.warn('deprecated: Use validateServiceConfig() instead') - } - - /** - * @deprecated Use validateServiceConfig() instead - */ - validateSubscriptionDefinitions() { - // eslint-disable-next-line no-console - console.warn('deprecated: Use validateServiceConfig() instead') - } +export class ServiceBuilder { + private commandDefinitionList: CommandDefinitionList = [] + private subscriptionDefinitionList: SubscriptionDefinitionList = [] + + private commandDefinitionListResolved: CommandDefinitionListResolved = [] + private subscriptionDefinitionListResolved: SubscriptionDefinitionListResolved = [] + + private configSchema?: Schema + private defaultConfig?: Complete + + private definitionsResolved = false + + private deprecated = false + + private requiresResources = false + + SClass: Newable> = Service + + // eslint-disable-next-line no-useless-constructor + constructor(public info: ServiceInfoType) {} + + /** + * "This function sets the config schema for the service builder." + * + * @param schema - The schema that will be used to validate the config. + * @returns ServiceBuilder + */ + setConfigSchema(schema: T) { + this.configSchema = schema + return this as unknown as ServiceBuilder< + SetNewTypeValues< + S, + { + ConfigType: Infer extends Record ? Infer : NeverObject + ConfigInputType: InferIn extends Record ? InferIn : NeverObject + ServiceClassType: Service< + ServiceClassTypes extends Record ? Infer : EmptyObject, S['Resources']> + > + } + > + > + } + + /** + * "This function sets the default configuration for the service." + * + * @param config - ConfigType - The default configuration for the service. + * @returns The ServiceBuilder instance + * @deprecated Use a default value in the config validation schema instead + */ + setDefaultConfig(config: Complete): this { + this.defaultConfig = config + return this + } + + /** + * Mark this service as deprecated + * @returns The ServiceBuilder instance + */ + markAsDeprecated() { + this.deprecated = true + return this + } + + /** + * `addCommandDefinition` adds a list of command definitions to the service builder + * @param commands - CommandDefinitionList + * @returns The service builder + */ + addCommandDefinition(...commands: CommandDefinitionList) { + if (this.definitionsResolved) { + throw new UnhandledError( + StatusCode.InternalServerError, + 'You can not add commands after resolveDefinitions is called.', + ) + } + this.commandDefinitionList.push(...commands) + return this + } + + /** + * It adds a subscription definition to the service builder + * @param subscription - SubscriptionDefinitionList + * @returns The service builder + */ + addSubscriptionDefinition(...subscription: SubscriptionDefinitionList) { + if (this.definitionsResolved) { + throw new UnhandledError( + StatusCode.InternalServerError, + 'You can not add subscriptions after resolveDefinitions is called.', + ) + } + this.subscriptionDefinitionList.push(...subscription) + return this + } + + /** + * + * Resolves the command and subscription definitions + */ + public async resolveDefinitions() { + if (this.definitionsResolved) { + return { + commands: this.commandDefinitionListResolved, + subscriptions: this.subscriptionDefinitionListResolved, + } + } + + this.commandDefinitionListResolved = await Promise.all(this.commandDefinitionList) + this.subscriptionDefinitionListResolved = await Promise.all(this.subscriptionDefinitionList) + + this.subscriptionDefinitionList = [] + this.commandDefinitionList = [] + + this.definitionsResolved = true + return { + commands: this.commandDefinitionListResolved, + subscriptions: this.subscriptionDefinitionListResolved, + } + } + + /** + * Define the resources of the service. + * Resources are available within commands and subscriptions. + * + * @example + * ```ts + * serviceBuilder.defineResources<'resource_name',ResourceType>() + * ``` + * + * @returns The builder with defined types for resources + */ + defineResource() { + this.requiresResources = true + return this as unknown as ServiceBuilder< + SetNewTypeValue }> + > + } + + /** + * It sets the class type of the service. + * @param customClass - A class which extends the Service class + * @returns The builder itself, but with the type of the service class changed. + */ + setCustomClass>>( + customClass: Newable>, + ) { + this.SClass = customClass + return this as unknown as ServiceBuilder> + } + + getCustomClass() { + return this.SClass + } + + /** + * It creates a new instance of the service class, passing in the logger, service info, event bridge, + * command functions, subscription list, and configuration + * @param eventBridge - EventBridge + * @param options - additional config like logger, stores and opentelemetry span processor + * @returns The instance of the service class + */ + async getInstance(eventBridge: EventBridge, options?: InstanceConfigType) { + const logger = options?.logger ?? initLogger(options?.logLevel) + + const cfg: S['ConfigInputType'] = { + ...this.defaultConfig, + ...options?.serviceConfig, + } + + let config: S['ConfigType'] = cfg as S['ConfigType'] + if (this.configSchema) { + const validationResult = await validate(this.configSchema, cfg) + if (!validationResult.success) { + const err = new UnhandledError( + StatusCode.InternalServerError, + 'The given service configuration is invalid', + validationResult.issues, + ) + logger.error({ err }, err.message) + throw err + } + config = validationResult.data as S['ConfigType'] + } + + if (this.requiresResources && !options?.resources) { + const err = new UnhandledError( + StatusCode.InternalServerError, + 'This services requires resources to be set in getInstance options', + ) + logger.error({ err }, err.message) + throw err + } + + const secretStore: SecretStore = + options?.secretStore ?? + initDefaultSecretStore({ + logger, + }) + + const configStore: ConfigStore = + options?.configStore ?? + initDefaultConfigStore({ + logger, + }) + + const stateStore: StateStore = + options?.stateStore ?? + initDefaultStateStore({ + logger, + }) + + const { commands, subscriptions } = await this.resolveDefinitions() + + const C = this.getCustomClass() + + return new C({ + logger, + eventBridge, + info: this.info, + commandDefinitionList: commands, + subscriptionDefinitionList: subscriptions, + config, + spanProcessor: options?.spanProcessor, + secretStore, + configStore, + stateStore, + configSchema: this.configSchema, + resources: options?.resources, + }) + } + + /** + * It returns a new instance of the CommandDefinitionBuilder class, which is a class that is used to + * build a command definition + * @param commandName - The name of the command. + * @param description - The description of the command. + * @param eventName - The name of the event that will be emitted when the command is + * executed. + * @returns A CommandDefinitionBuilder object. + */ + getCommandBuilder( + commandName: NonEmptyString, + description: string, + eventName?: NonEmptyString, + ) { + return new CommandDefinitionBuilder< + S['ServiceClassType'], + CommandDefinitionBuilderTypes< + Schema, + Schema, + Schema, + Schema, + Schema, + Schema, + S['Resources'], + InvokeList, + Record + > + >(commandName, description, eventName, this.deprecated) + } + + /** + * It returns a new instance of the `SubscriptionDefinitionBuilder` class, which is a class that is + * used to build a subscription definition + * @param subscriptionName - The name of the subscription. + * @param description - The description of the subscription. + * @returns A SubscriptionDefinitionBuilder + */ + getSubscriptionBuilder( + subscriptionName: NonEmptyString, + description: string, + ): SubscriptionDefinitionBuilder< + S['ServiceClassType'], + SubscriptionDefinitionBuilderTypes + > { + return new SubscriptionDefinitionBuilder< + S['ServiceClassType'], + SubscriptionDefinitionBuilderTypes + >(subscriptionName, description, this.deprecated) + } + + /** + * @returns the definition of registered commands + */ + getCommandDefinitions() { + if (!this.resolveDefinitions) { + throw new UnhandledError( + StatusCode.InternalServerError, + 'Definitions not resolve. Please call resolveDefinitions() before using getCommandDefinitions', + ) + } + return this.commandDefinitionListResolved + } + + /** + * @returns the definition of registered subscriptions + */ + getSubscriptionDefinitions() { + if (!this.resolveDefinitions) { + throw new UnhandledError( + StatusCode.InternalServerError, + 'Definitions not resolve. Please call resolveDefinitions() before using getCommandDefinitions', + ) + } + return this.subscriptionDefinitionListResolved + } + + /** + * A simple test helper, which ensures, that there ar no duplicate names used. + */ + async testServiceSetup() { + const { subscriptions, commands } = await this.resolveDefinitions() + + this.validateCommands(commands) + this.validateSubscriptions(subscriptions) + + return true + } + + protected validateCommands(commandDefinitions: CommandDefinitionListResolved) { + const existingNames = new Set() + const eventNames = new Set() + + for (const definition of commandDefinitions) { + const name = definition.commandName.toLowerCase().trim() + const eventName = definition.eventName + + // check for duplicate command names + if (existingNames.has(name)) { + fail(`duplicate command name ${name}`) + } + existingNames.add(name) + + // check for duplicate event names + if (eventName) { + if (eventNames.has(eventName)) { + fail(`response event "${eventName}" in ${name} is used in other command`) + } + eventNames.add(eventName) + } + } + } + + /** + * Returns the service definition. + * This inclues information about commands and subscriptions. + * + * @returns + */ + async getFullServiceDefintion() { + const definitions = await this.resolveDefinitions() + + return { + ...this.info, + ...definitions, + deprecated: this.deprecated, + } + } + + protected validateSubscriptions(subscriptionDefinitions: SubscriptionDefinitionListResolved) { + const existingNames = new Set() + for (const definition of subscriptionDefinitions) { + const name = definition.subscriptionName.toLowerCase().trim() + + if (existingNames.has(name)) { + fail(`duplicate subscription name ${name}`) + } + existingNames.add(name) + } + } + + /** + * @deprecated Use testServiceSetup() instead + */ + validateCommandDefinitions() { + // biome-ignore lint/suspicious/noConsole: no logger available + console.warn('deprecated: Use testServiceSetup() instead') + } + + /** + * @deprecated Use testServiceSetup() instead + */ + validateSubscriptionDefinitions() { + // biome-ignore lint/suspicious/noConsole: no logger available + console.warn('deprecated: Use testServiceSetup() instead') + } } diff --git a/packages/core/src/ServiceBuilder/serviceBuilder.test.ts b/packages/core/src/ServiceBuilder/serviceBuilder.test.ts index fdd083c4a..bf01e78f8 100644 --- a/packages/core/src/ServiceBuilder/serviceBuilder.test.ts +++ b/packages/core/src/ServiceBuilder/serviceBuilder.test.ts @@ -1,84 +1,69 @@ import { createSandbox } from 'sinon' -import { z } from 'zod' import { CommandDefinitionBuilder } from '../CommandDefinitionBuilder/index.js' +import { SubscriptionDefinitionBuilder } from '../SubscriptionDefinitionBuilder/index.js' import type { ServiceInfoType } from '../core/index.js' import { Service } from '../core/index.js' import { getEventBridgeMock, getLoggerMock } from '../mocks/index.js' -import { SubscriptionDefinitionBuilder } from '../SubscriptionDefinitionBuilder/index.js' import { ServiceBuilder } from './ServiceBuilder.impl.js' describe('ServiceBuilder', () => { - const serviceInfo: ServiceInfoType = { - serviceName: 'test-service', - serviceVersion: '1', - serviceDescription: 'the description of the service', - } - - const sandbox = createSandbox() - - afterEach(() => { - sandbox.reset() - }) - - it('can set a service config schema and default config', async () => { - const service = new ServiceBuilder(serviceInfo) - - const configSchema = z.object({ - host: z.string(), - }) - - const defaultConfig = { - host: 'localhost', - } - - const serviceWithConfigSchema = service.setConfigSchema(configSchema) - const serviceWithDefaultConfig = serviceWithConfigSchema.setDefaultConfig(defaultConfig) - - const eventBridge = getEventBridgeMock(sandbox) - const logger = getLoggerMock(sandbox) - - const serviceInstanceWithDefaultConfig = await serviceWithDefaultConfig.getInstance(eventBridge.mock, { - logger: logger.mock, - }) - - expect(serviceInstanceWithDefaultConfig.config.host).toEqual(defaultConfig.host) - - const serviceInstanceWithCustomConfig = await serviceWithDefaultConfig.getInstance(eventBridge.mock, { - logger: logger.mock, - serviceConfig: { host: 'remote' }, - }) - - expect(serviceInstanceWithCustomConfig.config.host).toBe('remote') - }) - - it('returns a CommandBuilder', () => { - const service = new ServiceBuilder(serviceInfo) - expect(service.getCommandBuilder('command-name', 'command description')).toBeInstanceOf(CommandDefinitionBuilder) - }) - - it('returns a SubscriptionBuilder', () => { - const service = new ServiceBuilder(serviceInfo) - expect(service.getSubscriptionBuilder('command-name', 'command description')).toBeInstanceOf( - SubscriptionDefinitionBuilder, - ) - }) - - it('can use a custom service class', async () => { - class CustomClass extends Service<{}> { - customFunction() { - return 'custom' - } - } - - const service = new ServiceBuilder(serviceInfo).setCustomClass(CustomClass) - - const eventBridge = getEventBridgeMock(sandbox) - const logger = getLoggerMock(sandbox) - - const serviceInstance = await service.getInstance(eventBridge.mock, { logger: logger.mock }) - - expect(serviceInstance.customFunction()).toBe('custom') - expect(serviceInstance).toBeInstanceOf(CustomClass) - }) + const serviceInfo: ServiceInfoType = { + serviceName: 'test-service', + serviceVersion: '1', + serviceDescription: 'the description of the service', + } + + const sandbox = createSandbox() + + afterEach(() => { + sandbox.reset() + }) + + it('returns a CommandBuilder', () => { + const service = new ServiceBuilder(serviceInfo) + expect(service.getCommandBuilder('command-name', 'command description')).toBeInstanceOf(CommandDefinitionBuilder) + }) + + it('returns a SubscriptionBuilder', () => { + const service = new ServiceBuilder(serviceInfo) + expect(service.getSubscriptionBuilder('command-name', 'command description')).toBeInstanceOf( + SubscriptionDefinitionBuilder, + ) + }) + + it('can use a custom service class', async () => { + class CustomClass extends Service { + customFunction() { + return 'custom' + } + } + + const service = new ServiceBuilder(serviceInfo).setCustomClass(CustomClass) + + const eventBridge = getEventBridgeMock(sandbox) + const logger = getLoggerMock(sandbox) + + const serviceInstance = await service.getInstance(eventBridge.mock, { logger: logger.mock }) + + expect(serviceInstance.customFunction()).toBe('custom') + expect(serviceInstance).toBeInstanceOf(CustomClass) + }) + + it('can add resources', async () => { + class ExampleClass { + method() { + return 'hello' + } + } + + const service = new ServiceBuilder(serviceInfo).defineResource<'x', ExampleClass>() + + const eventBridge = getEventBridgeMock(sandbox) + const logger = getLoggerMock(sandbox) + const _serviceInstance = await service.getInstance(eventBridge.mock, { + logger: logger.mock, + resources: { x: new ExampleClass() }, + }) + }) }) diff --git a/packages/core/src/SubscriptionDefinitionBuilder/SubscriptionDefinitionBuilder.impl.ts b/packages/core/src/SubscriptionDefinitionBuilder/SubscriptionDefinitionBuilder.impl.ts index 8595addf6..10c44ac99 100644 --- a/packages/core/src/SubscriptionDefinitionBuilder/SubscriptionDefinitionBuilder.impl.ts +++ b/packages/core/src/SubscriptionDefinitionBuilder/SubscriptionDefinitionBuilder.impl.ts @@ -1,31 +1,33 @@ import type { Infer, InferIn, Schema } from '@typeschema/main' import type { SinonSandbox } from 'sinon' -import type { ZodAny } from 'zod' -import { - type Complete, - type ContentType, - type DefinitionEventBridgeConfig, - type EBMessage, - type EBMessageType, - type FromInvokeToOtherType, - type InstanceId, - type PrincipalId, - type ServiceClass, - StatusCode, - type SubscriptionAfterGuardHook, - type SubscriptionBeforeGuardHook, - type SubscriptionDefinition, - type SubscriptionDefinitionMetadataBase, - type SubscriptionFunction, - type SubscriptionTransformInputHook, - type SubscriptionTransformOutputHook, - type TenantId, - UnhandledError, +import type { + Complete, + ContentType, + DefinitionEventBridgeConfig, + EBMessage, + EBMessageType, + InstanceId, + PrincipalId, + Service, + SubscriptionAfterGuardHook, + SubscriptionBeforeGuardHook, + SubscriptionDefinition, + SubscriptionDefinitionMetadataBase, + SubscriptionFunction, + SubscriptionTransformInputHook, + SubscriptionTransformOutputHook, + TenantId, } from '../core/index.js' -import type { NonEmptyString } from '../helper/index.js' +import { StatusCode, UnhandledError } from '../core/index.js' +import { + type NonEmptyString, + convertEmitValidationsToSchema, + convertInvokeValidationsToSchema, +} from '../helper/index.js' import { getSubscriptionContextMock, getSubscriptionTransformContextMock } from '../mocks/index.js' import { validationToSchema } from '../zodOpenApi/index.js' +import type { SubscriptionDefinitionBuilderTypes } from './SubscriptionDefinitionBuilderTypes.js' import { getSubscriptionFunctionWithValidation } from './getSubscriptionFunctionWithValidation.impl.js' /** @@ -37,837 +39,820 @@ import { getSubscriptionFunctionWithValidation } from './getSubscriptionFunction * @group Subscription */ export class SubscriptionDefinitionBuilder< - ServiceClassType extends ServiceClass = ServiceClass, - MessagePayloadType = unknown, - MessageParamsType = undefined, - MessageResultType = void, - PayloadSchema extends Schema = ZodAny, - ParameterSchema extends Schema = ZodAny, - ResultSchema extends Schema = ZodAny, - Invokes = {}, - EmitListType = {}, + S extends Service = Service, + C extends SubscriptionDefinitionBuilderTypes = SubscriptionDefinitionBuilderTypes, > { - private messageType: EBMessageType | undefined - - private inputSchema?: PayloadSchema - private inputContentType: ContentType | undefined - private inputContentEncoding: string | undefined - private outputSchema?: ResultSchema - private outputContentType: ContentType | undefined - private outputContentEncoding: string | undefined - private parameterSchema?: ParameterSchema - - private hooks: { - transformInput?: { - transformInputSchema: Schema - transformParameterSchema: Schema - transformFunction: SubscriptionTransformInputHook - } - beforeGuard: Record< - string, - SubscriptionBeforeGuardHook, Infer, Invokes, EmitListType> - > - afterGuard: Record< - string, - SubscriptionAfterGuardHook< - ServiceClassType, - Infer, - Infer, - Infer, - Invokes, - EmitListType - > - > - transformOutput?: { - transformOutputSchema: Schema - transformFunction: SubscriptionTransformOutputHook< - ServiceClassType, - Infer, - Infer, - any - > - } - } = { - transformInput: undefined, - beforeGuard: {}, - afterGuard: {}, - transformOutput: undefined, - } - - private sender?: { - serviceName?: string - serviceVersion?: string - serviceTarget?: string - instanceId?: InstanceId - } - - private receiver?: { - serviceName?: string - serviceVersion?: string - serviceTarget?: string - instanceId?: InstanceId - } - - private fn?: SubscriptionFunction< - ServiceClassType, - MessagePayloadType, - MessageParamsType, - Infer, - Infer, - InferIn, - Invokes, - EmitListType - > - - private eventName?: string - private emitEventName?: string - - private principalId?: PrincipalId - private tenantId?: TenantId - - private durable = true - - private shared = true - private autoacknowledge = false - - private invokes: FromInvokeToOtherType< - Invokes, - { outputSchema?: Schema; payloadSchema?: Schema; parameterSchema?: Schema } - > = {} as FromInvokeToOtherType - - private emitList: EmitListType = {} as EmitListType - - // eslint-disable-next-line no-useless-constructor - constructor( - private subscriptionName: Exclude, - private subscriptionDescription: string, - ) {} - - canInvoke< - Output extends Schema, - Payload extends Schema, - Parameter extends Schema, - SName extends string = string, - Version extends string = string, - Fname extends string = string, - >( - serviceName: SName, - serviceVersion: Version, - serviceTarget: Fname, - outputSchema?: Output, - payloadSchema?: Payload, - parameterSchema?: Parameter, - ) { - if (serviceName.trim() === '' || serviceVersion.trim() === '' || serviceTarget.trim() === '') { - throw new Error('canInvoke requires non-empty service name, version and target') - } - - const x = this.invokes as any - if (!x[serviceName]) { - x[serviceName] = {} - } - - if (!x[serviceName][serviceVersion]) { - x[serviceName][serviceVersion] = {} - } - - const f = { - [serviceName]: { - [serviceVersion]: { - [serviceTarget]: { outputSchema, payloadSchema, parameterSchema }, - }, - }, - } as Invokes & - Record< - SName, - Record< - Version, - Record, parameter: InferIn) => Promise>> - > - > - - this.invokes = { - ...this.invokes, - ...f, - } - - return this as SubscriptionDefinitionBuilder< - ServiceClassType, - MessagePayloadType, - MessageParamsType, - MessageResultType, - PayloadSchema, - ParameterSchema, - ResultSchema, - Invokes & - Record< - SName, - Record< - Version, - Record, parameter: InferIn) => Promise>> - > - >, - EmitListType - > - } - - /** - * Define which custom events the command can emit. - * - * @param eventName The custom event name - * @param schema the payload schema - * @returns - */ - canEmit(eventName: EventName, schema: T) { - if (eventName.trim() === '') { - throw new Error('canEmit requires non-empty event name') - } - - this.emitList = { ...this.emitList, [eventName]: schema } - - return this as SubscriptionDefinitionBuilder< - ServiceClassType, - MessagePayloadType, - MessageParamsType, - MessageResultType, - PayloadSchema, - ParameterSchema, - ResultSchema, - Invokes, - EmitListType & Record> - > - } - - /** - * Add a filter to only subscribe to messages with matching event name - * @param eventName The name of event to subscribe - * @param serviceVersion the version of the service that produces the event - * @returns SubscriptionDefinitionBuilder - */ - subscribeToEvent( - eventName: NonEmptyString, - serviceVersion?: NonEmptyString, - ) { - this.eventName = eventName - this.sender = this.sender ?? {} - this.sender.serviceVersion = serviceVersion - return this - } - - /** - * Filter messages only for principalId - * @param principalId the principal id to subscribe - * @returns - */ - filterPrincipalId(principalId: NonEmptyString) { - this.principalId = principalId - return this - } - - /** - * Filter messages only for tenantId - * @param tenantId the principal id to subscribe - * @returns - */ - filterTenantId(tenantId: NonEmptyString) { - this.tenantId = tenantId - return this - } - - /** - * Instruct the event bridge message broker to autoacknowledge messages as soon as they arrive. - * This means, a message will not be resent, if the subscription execution fails unexpected. - * - * If set to false, the message will be resent from message broker to eventbridge, if: - * - the underlaying message broker supports it - * - if the subscription execution fails unexpected - * - if sending of optional subscription response failed - * - * @param acknowledge Enable (true) and disable (false) - * @returns SubscriptionDefinition - */ - adviceAutoacknowledgeMessage(acknowledge = true) { - this.autoacknowledge = acknowledge - return this - } - - /** - * Instruct the event bridge message broker to send the matching message to every running instance. - * The underlaying message broker must support this functionality. - * - * In serverless environments, this flag should not have any effect - * - * @param shared - * @returns SubscriptionDefinition - */ - receiveMessageOnEveryInstance(enforce = true) { - this.shared = !enforce - return this - } - - /** - * False: defines the subscription as a live-subscription, which is only able to process messages while the subscription itself is running. - * - * True: Advises the event bridge (like rabbitMQ) to store all messages if the subscription is not running. - * As soon as the subscription is back again, all missed messages will be sent first, before it starts working like a live-subscription. - */ - adviceDurable(durable: boolean) { - this.durable = durable - return this - } - - /** - * Add filter to only match messages send by given service function & version. - * Set one or more parameters to undefined means "do not filter by this criteria". - * For example: - * - * This will filter for all messages send from function testFunction of service UserService. - * This will include messages from all versions of this function. - * - * ```typescript - * sentFrom('UserService', undefined, 'testFunction') - * ``` - * - * @param serviceName the name of the service that produces the message - * @param serviceVersion the version of the service that produces the message - * @param serviceTarget the command or subscription name of the service that produces the message - * @param instanceId the event bridge instance id which was publishing the message - * @returns - */ - filterSentFrom( - serviceName: NonEmptyString | undefined, - serviceVersion: NonEmptyString | undefined, - serviceTarget: NonEmptyString | undefined, - instanceId: NonEmptyString | undefined, - ) { - this.sender = { - serviceName, - serviceVersion, - serviceTarget, - instanceId, - } - return this - } - - /** - * Add filter to only match messages received by given service function & version. - * Set one or more parameters to undefined means "do not filter by this criteria". - * For example: - * - * This will filter for all messages send to function testFunction of service UserService. - * This will include messages from all versions of this function. - * - * ```typescript - * receivedBy('UserService', undefined, 'testFunction') - * ``` - * - * @param serviceName the name of the service that consumes the message - * @param serviceVersion the version of the service that consumes the message - * @param serviceTarget the command or subscription name of the service that consumes the message - * @param instanceId the event bridge instance id which should receive the message - * @returns - */ - filterReceivedBy( - serviceName: NonEmptyString | undefined, - serviceVersion: NonEmptyString | undefined, - serviceTarget: NonEmptyString | undefined, - instanceId: NonEmptyString | undefined, - ) { - this.receiver = { - serviceName, - serviceVersion, - serviceTarget, - instanceId, - } - return this - } - - /** - * Adds a filter to match specific message type. - * - * Common message types are `Command`, `CommandSuccessResponse` and `CommandErrorResponse`. - * - * See @enum EBMessageType for full available list. - * - * @param messageType the type of message - * @returns - */ - filterForMessageType(messageType: EBMessageType) { - this.messageType = messageType - - return this - } - - /** - * Add a schema for input payload validation. - * Types for payload of message and function payload input are generated from given schema. - * @param inputSchema the validation schema for input payload - * @param inputContentType optional the content type of payload - * @param inputContentEncoding optional the content encoding - * @returns SubscriptionDefinitionBuilder - */ - addPayloadSchema( - inputSchema: T, - inputContentType = 'application/json', - inputContentEncoding = 'utf-8', - ) { - this.inputContentType = inputContentType || this.inputContentType - this.inputContentEncoding = inputContentEncoding || this.inputContentEncoding - - this.inputSchema = inputSchema as unknown as PayloadSchema - return this as unknown as SubscriptionDefinitionBuilder< - ServiceClassType, - InferIn, - MessageParamsType, - MessageResultType, - T, - ParameterSchema, - ResultSchema, - Invokes, - EmitListType - > - } - - /** - * Add a schema for output payload validation. - * Types for payload of message and function payload output are generated from given schema. - * @param eventName the event name to be used when the subscription result is emitted as custom event - * @param outputSchema the validation schema for the output payload - * @param outputContentType optional the content type of payload - * @param outputContentEncoding optional the content encoding - * @returns SubscriptionDefinitionBuilder - */ - addOutputSchema( - eventName: string, - outputSchema: T, - outputContentType = 'application/json', - outputContentEncoding = 'utf-8', - ) { - this.emitEventName = eventName - this.outputContentEncoding = outputContentEncoding - this.outputContentType = outputContentType - this.outputSchema = outputSchema as unknown as ResultSchema - return this as unknown as SubscriptionDefinitionBuilder< - ServiceClassType, - MessagePayloadType, - MessageParamsType, - T, - PayloadSchema, - ParameterSchema, - T, - Invokes, - EmitListType - > - } - - /** - * Add a schema for output parameter validation. - * Types for parameter of message and function parameter output are generated from given schema. - * @param parameterSchema the validation schema for output parameter - * @returns SubscriptionDefinitionBuilder - */ - addParameterSchema(parameterSchema: T) { - this.parameterSchema = parameterSchema as unknown as ParameterSchema - return this as unknown as SubscriptionDefinitionBuilder< - ServiceClassType, - MessagePayloadType, - InferIn, - MessageResultType, - PayloadSchema, - T, - ResultSchema, - Invokes, - EmitListType - > - } - - /** - * Set a transform input hook which will encode or transform the input payload and parameters. - * Will be executed as first step before input validation, before guard and the function itself. - * This will change the type of input message payload and input message parameter. - * @param transformInputSchema Input payload validation schema - * @param transformParameterSchema Input parameter validation schema - * @param transformFunction the transform input function - * @param inputContentType optional the content type of payload - * @param inputContentEncoding optional the content encoding - * @returns SubscriptionDefinitionBuilder - */ - setTransformInput( - transformInputSchema: TransFormPayloadSchema, - transformParameterSchema: TransFormParameterSchema, - transformFunction: SubscriptionTransformInputHook< - ServiceClassType, - InferIn, - InferIn, - InferIn, - InferIn - >, - inputContentType?: ContentType, - inputContentEncoding?: string, - ) { - this.inputContentType = inputContentType ?? this.inputContentType - this.inputContentEncoding = inputContentEncoding ?? this.inputContentEncoding - - this.hooks.transformInput = { - transformFunction, - transformInputSchema, - transformParameterSchema, - } - return this as unknown as SubscriptionDefinitionBuilder< - ServiceClassType, - InferIn, - InferIn, - MessageResultType, - PayloadSchema, - ParameterSchema, - ResultSchema, - Invokes, - EmitListType - > - } - - /** - * Return the transform input function - * @returns the input transform function if defined - */ - getTransformInputFunction() { - if (!this.hooks.transformInput) { - return undefined - } - - return this.hooks.transformInput.transformFunction as SubscriptionTransformInputHook< - ServiceClassType, - InferIn, - InferIn, - MessagePayloadType, - MessageParamsType - > - } - - /** - * Set a transform output hook which will encode or transform the response payload. - * Will be executed at very last step after function execution, output validation and after guard hooks. - * This will change the type of output message payload. - * @param transformOutputSchema The output validation schema - * @param transformFunction the transform output function - * @param outputContentType optional the content type of payload - * @param outputContentEncoding optional the content encoding - * @returns SubscriptionDefinitionBuilder - */ - setTransformOutput( - transformOutputSchema: Output, - transformFunction: SubscriptionTransformOutputHook< - ServiceClassType, - Infer, - Infer, - InferIn - >, - outputContentType?: ContentType, - outputContentEncoding?: string, - ) { - this.outputContentEncoding = outputContentEncoding ?? this.outputContentEncoding - this.outputContentType = outputContentType ?? this.outputContentType - - this.hooks.transformOutput = { - transformFunction, - transformOutputSchema, - } - return this as unknown as SubscriptionDefinitionBuilder< - ServiceClassType, - MessagePayloadType, - MessageParamsType, - Infer, - PayloadSchema, - ParameterSchema, - ResultSchema, - Invokes, - EmitListType - > - } - - /** - * Return the transform output function - * @returns the transform output function if defined - */ - getTransformOutputFunction() { - if (!this.hooks.transformOutput) { - return undefined - } - - return this.hooks.transformOutput.transformFunction as SubscriptionTransformOutputHook< - ServiceClassType, - Infer, - Infer, - Infer - > - } - - /** - * Set one or more before guard hook(s). - * If there are multiple before guard hooks, they are executed in parallel - * @param beforeGuards Object of key = name of guard, value = function - * @returns SubscriptionDefinitionBuilder - */ - setBeforeGuardHooks( - beforeGuards: Record< - string, - SubscriptionBeforeGuardHook, Infer, Invokes, EmitListType> - >, - ) { - this.hooks.beforeGuard = { ...this.hooks.beforeGuard, ...beforeGuards } - return this - } - - /** - * Set one or more after guard hook(s). - * If there are multiple after guard hooks, they are executed in parallel - * @param afterGuard Object of key = name of guard, value = function - * @returns SubscriptionDefinitionBuilder - */ - setAfterGuardHooks( - afterGuards: Record< - string, - SubscriptionAfterGuardHook< - ServiceClassType, - Infer, - Infer, - Infer, - Invokes, - EmitListType - > - >, - ) { - this.hooks.afterGuard = { ...this.hooks.afterGuard, ...afterGuards } - return this - } - - /** - * Required: Set the function implementation. - * The types should be automatically set as soon as schemas previously defined. - * As the function will be a a function of a service class you need to implement as function declaration. - * Anonymous functions do not have access to the `this` scope. - * - * @example - * ```ts - * async function (context, payload, parameter) { - * - * return `the result output payload` - * } - * ``` - * @param fn the function implementation - * @returns SubscriptionDefinitionBuilder - */ - public setSubscriptionFunction( - fn: SubscriptionFunction< - ServiceClassType, - MessagePayloadType, - MessageParamsType, - Infer, - Infer, - Infer, - Invokes, - EmitListType - >, - ): SubscriptionDefinitionBuilder< - ServiceClassType, - MessagePayloadType, - MessageParamsType, - MessageResultType, - PayloadSchema, - ParameterSchema, - ResultSchema, - Invokes, - EmitListType - > { - this.fn = fn as unknown as SubscriptionFunction< - ServiceClassType, - MessagePayloadType, - MessageParamsType, - Infer, - Infer, - Infer, - Invokes, - EmitListType - > - - return this as unknown as SubscriptionDefinitionBuilder< - ServiceClassType, - MessagePayloadType, - MessageParamsType, - MessageResultType, - PayloadSchema, - ParameterSchema, - ResultSchema, - Invokes, - EmitListType - > - } - - /** - * Get the function implementation including input and output validation. - * Also, before and after hooks are triggered during execution. - * - * @returns the subscription function - */ - getSubscriptionFunction() { - if (!this.fn) { - throw new UnhandledError(StatusCode.NotImplemented, `No function implementation for ${this.subscriptionName}`, { - subscriptionName: this.subscriptionName, - }) - } - - const f: SubscriptionFunction< - ServiceClassType, - MessagePayloadType, - MessageParamsType, - InferIn, - InferIn, - Infer, - Invokes, - EmitListType - > = getSubscriptionFunctionWithValidation< - ServiceClassType, - MessagePayloadType, - MessageParamsType, - MessageResultType, - InferIn, - InferIn, - Infer, - Invokes, - EmitListType - >(this.fn, this.inputSchema, this.parameterSchema, this.outputSchema, this.hooks.beforeGuard) - - return f - } - - /** - * Get the function implementation without input and output validation. - * No hooks are triggered during execution. - * - * @returns the subscription function - */ - getSubscriptionFunctionPlain() { - if (!this.fn) { - throw new UnhandledError(StatusCode.NotImplemented, `No function implementation for ${this.subscriptionName}`, { - subscriptionName: this.subscriptionName, - }) - } - - this.fn as SubscriptionFunction< - ServiceClassType, - MessagePayloadType, - MessageParamsType, - InferIn, - InferIn, - Infer, - Invokes, - EmitListType - > - } - - /** - * Returns the final subscription definition which will be passed into the service class. - * @returns SubscriptionDefinition - */ - async getDefinition(): Promise< - SubscriptionDefinition< - ServiceClassType, - SubscriptionDefinitionMetadataBase, - MessagePayloadType, - MessageParamsType, - MessageResultType, - Infer, - Infer, - Infer, - Invokes, - EmitListType - > - > { - if (!this.fn) { - throw new Error(`SubscriptionDefinitionBuilder: missing function implementation for ${this.subscriptionName}`) - } - - const inputPayloadSchema: Schema | undefined = this.hooks.transformInput?.transformInputSchema ?? this.inputSchema - const inputParameterSchema: Schema | undefined = - this.hooks.transformInput?.transformParameterSchema ?? this.parameterSchema - const outputPayloadSchema: Schema | undefined = - this.hooks.transformOutput?.transformOutputSchema ?? this.outputSchema - - const eventBridgeConfig: Complete = { - durable: this.durable, - autoacknowledge: this.autoacknowledge, - shared: this.shared, - } - - const [inputPayload, parameter, outputPayload] = await Promise.all([ - validationToSchema(inputPayloadSchema), - validationToSchema(inputParameterSchema), - validationToSchema(outputPayloadSchema), - ]) - - const subscription: Complete< - SubscriptionDefinition< - ServiceClassType, - SubscriptionDefinitionMetadataBase, - MessagePayloadType, - MessageParamsType, - MessageResultType, - Infer, - Infer, - Infer, - Invokes, - EmitListType - > - > = { - subscriptionName: this.subscriptionName, - subscriptionDescription: this.subscriptionDescription, - eventBridgeConfig, - metadata: { - expose: { - contentTypeRequest: this.inputContentType, - contentEncodingRequest: this.inputContentEncoding, - contentTypeResponse: this.outputContentType, - contentEncodingResponse: this.outputContentEncoding, - inputPayload, - parameter, - outputPayload, - }, - }, - receiver: this.receiver, - sender: this.sender, - messageType: this.messageType, - eventName: this.eventName, - emitEventName: this.emitEventName, - principalId: this.principalId, - tenantId: this.tenantId, - call: this.getSubscriptionFunction(), - hooks: this.hooks, - invokes: this.invokes, - emitList: this.emitList, - } - - return subscription - } - - /** - * Returns a mocked command function context, which can be used in unit tests. - * - * @param message - * @param sandbox Sinon sandbox - * @returns a mocked command function context - */ - getSubscriptionContextMock(message: EBMessage, sandbox?: SinonSandbox) { - return getSubscriptionContextMock(message, sandbox, this.invokes, this.emitList) - } - - /** - * Returns a mocked transform function context, which can be used in unit tests. - * - * @param message - * @param sandbox Sinon sandbox - * @returns a mocked transform function context - */ - getSubscriptionTransformContextMock(message: EBMessage, sandbox?: SinonSandbox) { - return getSubscriptionTransformContextMock(message, sandbox) - } + private messageType: EBMessageType | undefined + + private inputSchema?: Schema + private inputContentType: ContentType | undefined + private inputContentEncoding: string | undefined + private outputSchema?: Schema + private outputContentType: ContentType | undefined + private outputContentEncoding: string | undefined + private parameterSchema?: Schema + + private hooks: { + transformInput?: { + transformInputSchema: Schema + transformParameterSchema: Schema + transformFunction: SubscriptionTransformInputHook + } + beforeGuard: Record> + afterGuard: Record> + transformOutput?: { + transformOutputSchema: Schema + transformFunction: SubscriptionTransformOutputHook + } + } = { + transformInput: undefined, + beforeGuard: {}, + afterGuard: {}, + transformOutput: undefined, + } + + private sender?: { + serviceName?: string + serviceVersion?: string + serviceTarget?: string + instanceId?: InstanceId + } + + private receiver?: { + serviceName?: string + serviceVersion?: string + serviceTarget?: string + instanceId?: InstanceId + } + + private fn?: SubscriptionFunction + + private eventName?: string + private emitEventName?: string + + private principalId?: PrincipalId + private tenantId?: TenantId + + private durable = true + + private shared = true + private autoacknowledge = false + + private invokes: C['Invokes'] = {} + + private emitList: C['EmitList'] = {} + + private deprecated = false + + constructor( + private subscriptionName: Exclude, + private subscriptionDescription: string, + + deprecated = false, + ) { + this.deprecated = deprecated + } + + /** + * Define a command which can be invoked by the current subscription + * @param serviceName + * @param serviceVersion + * @param serviceTarget + * @param outputSchema + * @param payloadSchema + * @param parameterSchema + * @returns + */ + canInvoke< + Output extends Schema, + Payload extends Schema, + Parameter extends Schema, + SName extends string = string, + Version extends string = string, + Fname extends string = string, + >( + serviceName: SName, + serviceVersion: Version, + serviceTarget: Fname, + outputSchema?: Output, + payloadSchema?: Payload, + parameterSchema?: Parameter, + ) { + if (serviceName.trim() === '' || serviceVersion.trim() === '' || serviceTarget.trim() === '') { + throw new Error('canInvoke requires non-empty service name, version and target') + } + + const x = this.invokes as any + if (!x[serviceName]) { + x[serviceName] = {} + } + + if (!x[serviceName][serviceVersion]) { + x[serviceName][serviceVersion] = {} + } + + const f = { + [serviceName]: { + [serviceVersion]: { + [serviceTarget]: { outputSchema, payloadSchema, parameterSchema }, + }, + }, + } as unknown as C['Invokes'] & + Record< + SName, + Record< + Version, + Record, parameter: InferIn) => Promise>> + > + > + + this.invokes = { + ...this.invokes, + ...f, + } + + return this as unknown as SubscriptionDefinitionBuilder< + S, + SubscriptionDefinitionBuilderTypes< + C['PayloadSchema'], + C['ParamsSchema'], + C['OutputSchema'], + C['TransformInputPayloadSchema'], + C['TransformInputParamsSchema'], + C['TransformOutputSchema'], + C['Resources'], + C['Invokes'] & + Record< + SName, + Record< + Version, + Record, parameter: InferIn) => Promise>> + > + >, + C['EmitList'] + > + > + } + + /** + * Define which custom events the subscription can emit. + * + * @param eventName The custom event name + * @param schema the payload schema + * @returns + */ + canEmit(eventName: EventName, schema: T) { + if (eventName.trim() === '') { + throw new Error('canEmit requires non-empty event name') + } + + this.emitList = { ...this.emitList, [eventName]: schema } + + return this as unknown as SubscriptionDefinitionBuilder< + S, + SubscriptionDefinitionBuilderTypes< + C['PayloadSchema'], + C['ParamsSchema'], + C['OutputSchema'], + C['TransformInputPayloadSchema'], + C['TransformInputParamsSchema'], + C['TransformOutputSchema'], + C['Resources'], + C['Invokes'], + C['EmitList'] & Record> + > + > + } + + /** + * Mark this subscription as deprecated + * @returns SubscriptionDefinitionBuilder + */ + markAsDeprecated() { + this.deprecated = true + return this + } + + /** + * Add a filter to only subscribe to messages with matching event name + * @param eventName The name of event to subscribe + * @param serviceVersion the version of the service that produces the event + * @returns SubscriptionDefinitionBuilder + */ + subscribeToEvent( + eventName: NonEmptyString, + serviceVersion?: NonEmptyString, + ) { + this.eventName = eventName + this.sender = this.sender ?? {} + this.sender.serviceVersion = serviceVersion + return this + } + + /** + * Filter messages only for principalId + * @param principalId the principal id to subscribe + * @returns + */ + filterPrincipalId(principalId: NonEmptyString) { + this.principalId = principalId + return this + } + + /** + * Filter messages only for tenantId + * @param tenantId the principal id to subscribe + * @returns + */ + filterTenantId(tenantId: NonEmptyString) { + this.tenantId = tenantId + return this + } + + /** + * Instruct the event bridge message broker to autoacknowledge messages as soon as they arrive. + * This means, a message will not be resent, if the subscription execution fails unexpected. + * + * If set to false, the message will be resent from message broker to eventbridge, if: + * - the underlaying message broker supports it + * - if the subscription execution fails unexpected + * - if sending of optional subscription response failed + * + * @param acknowledge Enable (true) and disable (false) + * @returns SubscriptionDefinition + */ + adviceAutoacknowledgeMessage(acknowledge = true) { + this.autoacknowledge = acknowledge + return this + } + + /** + * Instruct the event bridge message broker to send the matching message to every running instance. + * The underlaying message broker must support this functionality. + * + * In serverless environments, this flag should not have any effect + * + * @param shared + * @returns SubscriptionDefinition + */ + receiveMessageOnEveryInstance(enforce = true) { + this.shared = !enforce + return this + } + + /** + * False: defines the subscription as a live-subscription, which is only able to process messages while the subscription itself is running. + * + * True: Advises the event bridge (like rabbitMQ) to store all messages if the subscription is not running. + * As soon as the subscription is back again, all missed messages will be sent first, before it starts working like a live-subscription. + */ + adviceDurable(durable: boolean) { + this.durable = durable + return this + } + + /** + * Add filter to only match messages send by given service function & version. + * Set one or more parameters to undefined means "do not filter by this criteria". + * For example: + * + * This will filter for all messages send from function testFunction of service UserService. + * This will include messages from all versions of this function. + * + * ```typescript + * sentFrom('UserService', undefined, 'testFunction') + * ``` + * + * @param serviceName the name of the service that produces the message + * @param serviceVersion the version of the service that produces the message + * @param serviceTarget the command or subscription name of the service that produces the message + * @param instanceId the event bridge instance id which was publishing the message + * @returns + */ + filterSentFrom( + serviceName: NonEmptyString | undefined, + serviceVersion: NonEmptyString | undefined, + serviceTarget: NonEmptyString | undefined, + instanceId: NonEmptyString | undefined, + ) { + this.sender = { + serviceName, + serviceVersion, + serviceTarget, + instanceId, + } + return this + } + + /** + * Add filter to only match messages received by given service function & version. + * Set one or more parameters to undefined means "do not filter by this criteria". + * For example: + * + * This will filter for all messages send to function testFunction of service UserService. + * This will include messages from all versions of this function. + * + * ```typescript + * receivedBy('UserService', undefined, 'testFunction') + * ``` + * + * @param serviceName the name of the service that consumes the message + * @param serviceVersion the version of the service that consumes the message + * @param serviceTarget the command or subscription name of the service that consumes the message + * @param instanceId the event bridge instance id which should receive the message + * @returns + */ + filterReceivedBy( + serviceName: NonEmptyString | undefined, + serviceVersion: NonEmptyString | undefined, + serviceTarget: NonEmptyString | undefined, + instanceId: NonEmptyString | undefined, + ) { + this.receiver = { + serviceName, + serviceVersion, + serviceTarget, + instanceId, + } + return this + } + + /** + * Adds a filter to match specific message type. + * + * Common message types are `Command`, `CommandSuccessResponse` and `CommandErrorResponse`. + * + * See @enum EBMessageType for full available list. + * + * @param messageType the type of message + * @returns + */ + filterForMessageType(messageType: EBMessageType) { + this.messageType = messageType + + return this + } + + /** + * Add a schema for input payload validation. + * Types for payload of message and function payload input are generated from given schema. + * @param inputSchema the validation schema for input payload + * @param inputContentType optional the content type of payload + * @param inputContentEncoding optional the content encoding + * @returns SubscriptionDefinitionBuilder + */ + addPayloadSchema( + inputSchema: PayloadSchema, + inputContentType = 'application/json', + inputContentEncoding = 'utf-8', + ) { + this.inputContentType = inputContentType || this.inputContentType + this.inputContentEncoding = inputContentEncoding || this.inputContentEncoding + + this.inputSchema = inputSchema as unknown as PayloadSchema + return this as unknown as SubscriptionDefinitionBuilder< + S, + SubscriptionDefinitionBuilderTypes< + PayloadSchema, + C['ParamsSchema'], + C['OutputSchema'], + C['TransformInputPayloadSchema'], + C['TransformInputParamsSchema'], + C['TransformOutputSchema'], + C['Resources'], + C['Invokes'], + C['EmitList'] + > + > + } + + /** + * Add a schema for output payload validation. + * Types for payload of message and function payload output are generated from given schema. + * @param eventName the event name to be used when the subscription result is emitted as custom event + * @param outputSchema the validation schema for the output payload + * @param outputContentType optional the content type of payload + * @param outputContentEncoding optional the content encoding + * @returns SubscriptionDefinitionBuilder + */ + addOutputSchema( + eventName: string, + outputSchema: OutputSchema, + outputContentType = 'application/json', + outputContentEncoding = 'utf-8', + ) { + this.emitEventName = eventName + this.outputContentEncoding = outputContentEncoding + this.outputContentType = outputContentType + this.outputSchema = outputSchema + return this as unknown as SubscriptionDefinitionBuilder< + S, + SubscriptionDefinitionBuilderTypes< + C['PayloadSchema'], + C['ParamsSchema'], + OutputSchema, + C['TransformInputPayloadSchema'], + C['TransformInputParamsSchema'], + C['TransformOutputSchema'], + C['Resources'], + C['Invokes'], + C['EmitList'] + > + > + } + + /** + * Add a schema for output parameter validation. + * Types for parameter of message and function parameter output are generated from given schema. + * @param parameterSchema the validation schema for output parameter + * @returns SubscriptionDefinitionBuilder + */ + addParameterSchema(parameterSchema: ParamsSchema) { + this.parameterSchema = parameterSchema + return this as unknown as SubscriptionDefinitionBuilder< + S, + SubscriptionDefinitionBuilderTypes< + C['PayloadSchema'], + ParamsSchema, + C['OutputSchema'], + C['TransformInputPayloadSchema'], + C['TransformInputParamsSchema'], + C['TransformOutputSchema'], + C['Resources'], + C['Invokes'], + C['EmitList'] + > + > + } + + /** + * Set a transform input hook which will encode or transform the input payload and parameters. + * Will be executed as first step before input validation, before guard and the function itself. + * This will change the type of input message payload and input message parameter. + * @param transformInputSchema Input payload validation schema + * @param transformParameterSchema Input parameter validation schema + * @param transformFunction the transform input function + * @param inputContentType optional the content type of payload + * @param inputContentEncoding optional the content encoding + * @returns SubscriptionDefinitionBuilder + */ + setTransformInput( + transformInputSchema: TransformInputPayloadSchema, + transformParameterSchema: TransformInputParamsSchema, + transformFunction: SubscriptionTransformInputHook< + S, + Infer, + Infer, + InferIn, + InferIn + >, + inputContentType?: ContentType, + inputContentEncoding?: string, + ) { + this.inputContentType = inputContentType ?? this.inputContentType + this.inputContentEncoding = inputContentEncoding ?? this.inputContentEncoding + + this.hooks.transformInput = { + transformFunction, + transformInputSchema, + transformParameterSchema, + } + return this as unknown as SubscriptionDefinitionBuilder< + S, + SubscriptionDefinitionBuilderTypes< + C['PayloadSchema'], + C['ParamsSchema'], + C['OutputSchema'], + TransformInputPayloadSchema, + TransformInputParamsSchema, + C['TransformOutputSchema'], + C['Resources'], + C['Invokes'], + C['EmitList'] + > + > + } + + /** + * Return the transform input function + * @returns the input transform function if defined + */ + getTransformInputFunction() { + if (!this.hooks.transformInput) { + return undefined + } + + return this.hooks.transformInput.transformFunction as SubscriptionTransformInputHook< + S, + Infer, + Infer, + InferIn, + InferIn + > + } + + /** + * Set a transform output hook which will encode or transform the response payload. + * Will be executed at very last step after function execution, output validation and after guard hooks. + * This will change the type of output message payload. + * @param transformOutputSchema The output validation schema + * @param transformFunction the transform output function + * @param outputContentType optional the content type of payload + * @param outputContentEncoding optional the content encoding + * @returns SubscriptionDefinitionBuilder + */ + setTransformOutput( + transformOutputSchema: TransformOutputSchema, + transformFunction: SubscriptionTransformOutputHook< + S, + Infer, + Infer, + InferIn + >, + outputContentType?: ContentType, + outputContentEncoding?: string, + ) { + this.outputContentEncoding = outputContentEncoding ?? this.outputContentEncoding + this.outputContentType = outputContentType ?? this.outputContentType + + this.hooks.transformOutput = { + transformFunction, + transformOutputSchema, + } + return this as unknown as SubscriptionDefinitionBuilder< + S, + SubscriptionDefinitionBuilderTypes< + C['PayloadSchema'], + C['ParamsSchema'], + C['OutputSchema'], + C['TransformInputPayloadSchema'], + C['TransformInputParamsSchema'], + TransformOutputSchema, + C['Resources'], + C['Invokes'], + C['EmitList'] + > + > + } + + /** + * Return the transform output function + * @returns the transform output function if defined + */ + getTransformOutputFunction() { + if (!this.hooks.transformOutput) { + return undefined + } + + return this.hooks.transformOutput.transformFunction as SubscriptionTransformOutputHook< + S, + Infer, + Infer, + InferIn + > + } + + /** + * Set one or more before guard hook(s). + * If there are multiple before guard hooks, they are executed in parallel + * @param beforeGuards Object of key = name of guard, value = function + * @returns SubscriptionDefinitionBuilder + */ + setBeforeGuardHooks( + beforeGuards: Record< + string, + SubscriptionBeforeGuardHook< + S, + Infer, + Infer, + C['Resources'], + C['Invokes'], + C['EmitList'] + > + >, + ) { + this.hooks.beforeGuard = { ...this.hooks.beforeGuard, ...beforeGuards } + return this + } + + /** + * Set one or more after guard hook(s). + * If there are multiple after guard hooks, they are executed in parallel + * @param afterGuard Object of key = name of guard, value = function + * @returns SubscriptionDefinitionBuilder + */ + setAfterGuardHooks( + afterGuards: Record< + string, + SubscriptionAfterGuardHook< + S, + Infer, + Infer, + Infer, + C['Resources'], + C['Invokes'], + C['EmitList'] + > + >, + ) { + this.hooks.afterGuard = { ...this.hooks.afterGuard, ...afterGuards } + return this + } + + /** + * Required: Set the function implementation. + * The types should be automatically set as soon as schemas previously defined. + * As the function will be a a function of a service class you need to implement as function declaration. + * Anonymous functions do not have access to the `this` scope. + * + * @example + * ```ts + * async function (context, payload, parameter) { + * + * return `the result output payload` + * } + * ``` + * @param fn the function implementation + * @returns SubscriptionDefinitionBuilder + */ + public setSubscriptionFunction( + fn: SubscriptionFunction< + S, + Infer, + Infer, + InferIn, + C['Resources'], + C['Invokes'], + C['EmitList'] + >, + ) { + this.fn = fn + + return this + } + + /** + * Get the function implementation including input and output validation. + * Also, before and after hooks are triggered during execution. + * + * @returns the subscription function + */ + getSubscriptionFunction() { + if (!this.fn) { + throw new UnhandledError(StatusCode.NotImplemented, `No function implementation for ${this.subscriptionName}`, { + subscriptionName: this.subscriptionName, + }) + } + + return getSubscriptionFunctionWithValidation( + this.fn, + this.inputSchema, + this.parameterSchema, + this.outputSchema, + this.hooks.beforeGuard, + ) as SubscriptionFunction< + S, + InferIn, + InferIn, + InferIn, + C['Resources'], + C['Invokes'], + C['EmitList'] + > + } + + /** + * Get the function implementation without input and output validation. + * No hooks are triggered during execution. + * + * @returns the subscription function + */ + getSubscriptionFunctionPlain() { + if (!this.fn) { + throw new UnhandledError(StatusCode.NotImplemented, `No function implementation for ${this.subscriptionName}`, { + subscriptionName: this.subscriptionName, + }) + } + + this.fn as SubscriptionFunction< + S, + Infer, + Infer, + InferIn, + C['Resources'], + C['Invokes'], + C['EmitList'] + > + } + + /** + * Returns the final subscription definition which will be passed into the service class. + * @returns SubscriptionDefinition + */ + async getDefinition() { + if (!this.fn) { + throw new Error(`SubscriptionDefinitionBuilder: missing function implementation for ${this.subscriptionName}`) + } + + const inputPayloadSchema: Schema | undefined = this.hooks.transformInput?.transformInputSchema ?? this.inputSchema + const inputParameterSchema: Schema | undefined = + this.hooks.transformInput?.transformParameterSchema ?? this.parameterSchema + const outputPayloadSchema: Schema | undefined = + this.hooks.transformOutput?.transformOutputSchema ?? this.outputSchema + + const eventBridgeConfig: Complete = { + durable: this.durable, + autoacknowledge: this.autoacknowledge, + shared: this.shared, + } + + const [inputPayload, parameter, outputPayload, invokes, emitList] = await Promise.all([ + validationToSchema(inputPayloadSchema), + validationToSchema(inputParameterSchema), + validationToSchema(outputPayloadSchema), + convertInvokeValidationsToSchema(this.invokes), + convertEmitValidationsToSchema(this.emitList), + ]) + + const subscription: Complete< + SubscriptionDefinition< + S, + Infer, + Infer, + Infer, + Infer, + InferIn, + Infer, + InferIn, + C['Resources'], + C['Invokes'], + C['EmitList'], + SubscriptionDefinitionMetadataBase + > + > = { + subscriptionName: this.subscriptionName, + subscriptionDescription: this.subscriptionDescription, + eventBridgeConfig, + metadata: { + expose: { + contentTypeRequest: this.inputContentType, + contentEncodingRequest: this.inputContentEncoding, + contentTypeResponse: this.outputContentType, + contentEncodingResponse: this.outputContentEncoding, + inputPayload, + parameter, + outputPayload, + }, + }, + deprecated: this.deprecated, + receiver: this.receiver, + sender: this.sender, + messageType: this.messageType, + eventName: this.eventName, + emitEventName: this.emitEventName, + principalId: this.principalId, + tenantId: this.tenantId, + call: this.getSubscriptionFunction(), + hooks: this.hooks, + invokes, + emitList, + } + + return subscription + } + + /** + * Returns a mocked command function context, which can be used in unit tests. + * + * @param message + * @param sandbox Sinon sandbox + * @returns a mocked command function context + */ + getSubscriptionContextMock(input: { + message: EBMessage + resources?: Partial + sandbox?: SinonSandbox + }) { + return getSubscriptionContextMock({ + ...input, + invokes: this.invokes, + emitList: this.emitList, + }) + } + + /** + * Returns a mocked transform function context, which can be used in unit tests. + * + * @param message + * @param resources Resources to be used in the mock + * @param sandbox Sinon sandbox + * @returns a mocked transform function context + */ + getSubscriptionTransformContextMock(input: { + message: EBMessage + resources?: C['Resources'] + sandbox?: SinonSandbox + }) { + return getSubscriptionTransformContextMock(input) + } } diff --git a/packages/core/src/SubscriptionDefinitionBuilder/SubscriptionDefinitionBuilder.test.ts b/packages/core/src/SubscriptionDefinitionBuilder/SubscriptionDefinitionBuilder.test.ts index d079f0ea0..af2a6d31a 100644 --- a/packages/core/src/SubscriptionDefinitionBuilder/SubscriptionDefinitionBuilder.test.ts +++ b/packages/core/src/SubscriptionDefinitionBuilder/SubscriptionDefinitionBuilder.test.ts @@ -7,280 +7,280 @@ import { getCommandMessageMock, getEventBridgeMock, getLoggerMock } from '../moc import { SubscriptionDefinitionBuilder } from './SubscriptionDefinitionBuilder.impl.js' describe('SubscriptionDefinitionBuilder', () => { - const sandbox = createSandbox() - const service = new Service({ - info: { serviceName: 'TestService', serviceVersion: '1', serviceDescription: 'A service' }, - commandDefinitionList: [], - subscriptionDefinitionList: [], - logger: getLoggerMock(sandbox).mock, - eventBridge: getEventBridgeMock(sandbox).mock, - config: {}, - }) - - const functionPayloadSchema = z.object({ foo: z.string(), bar: z.number(), def: z.string().default('default_value') }) - const functionParameterSchema = z.object({ - paramOne: z.string(), - paramTwo: z.number(), - def: z.string().default('default_param'), - }) - const functionOutputSchema = z.object({ - result: z.object({ - payload: z.object({ foo: z.string(), bar: z.number(), other: z.string(), def: z.string() }), - parameter: z.object({ paramOne: z.string(), paramTwo: z.number(), def: z.string() }), - }), - }) - const transformPayloadSchema = z.string() - const transformParameterSchema = z.string() - const transformOutputSchema = z.string() - - const beforeOneStub = sandbox.stub() - const afterOneStub = sandbox.stub() - - const builder = new SubscriptionDefinitionBuilder('testSubscription', 'a unit test subscription') - .addPayloadSchema(functionPayloadSchema) - .addParameterSchema(functionParameterSchema) - .addOutputSchema('subscriptionEndEmitted', functionOutputSchema) - .setTransformInput(transformPayloadSchema, transformParameterSchema, async (_context, payload, parameter) => { - expect(typeof payload).toBe('string') - expect(typeof parameter).toBe('string') - - const pay: { - foo: string - bar: number - } = JSON.parse(payload) - const param: { - paramOne: string - paramTwo: number - } = JSON.parse(parameter) - - return { - payload: pay, - parameter: param, - } - }) - .setTransformOutput(transformOutputSchema, async (_context, payload, _parameter) => { - const p: Readonly<{ - result: { - payload: { - foo: string - bar: number - def: string - other: string - } - parameter: { - paramOne: string - paramTwo: number - } - } - }> = payload - - return JSON.stringify(p) - }) - .setBeforeGuardHooks({ - beforeOne: async (_context, payload, parameter) => { - const pay: { - foo: string - bar: number - def: string - } = payload - - const param: { - def: string - paramOne: string - paramTwo: number - } = parameter - beforeOneStub(pay, param) - }, - }) - .setAfterGuardHooks({ - afterOne: async (_context, fnOutputPayload, input, parameter) => { - const pay: { - foo: string - bar: number - def: string - } = input - - const param: { - def: string - paramOne: string - paramTwo: number - } = parameter - - const fnRes: { - result: { - payload: { - foo: string - bar: number - def: string - other: string - } - parameter: { - def: string - paramOne: string - paramTwo: number - } - } - } = fnOutputPayload - - afterOneStub(fnRes, pay, param) - }, - }) - .canInvoke( - 'OtherService', - '2', - 'testSubscription', - functionOutputSchema.merge(z.object({ toBeRemovedInResponse: z.string() })), - functionPayloadSchema, - functionParameterSchema, - ) - .canEmit('some', z.object({ example: z.string() })) - .setSubscriptionFunction(async (context, payload, parameter) => { - const result = await context.service.OtherService[2].testSubscription(payload, parameter) - - context.emit('some', { example: 'test' }) - - return result - }) - - const payload = { - foo: 'foo', - bar: 1, - } - const parameter = { - paramOne: 'Parameter 1', - paramTwo: 2, - } - - beforeEach(() => { - sandbox.reset() - }) - - afterAll(() => { - sandbox.restore() - }) - - it('does not throw on subscription function', async () => { - const subscriptionFunction = safeBind(builder.getSubscriptionFunction(), service) - - const msg = getCommandMessageMock({ - payload: { - payload, - parameter, - }, - }) - - const context = builder.getSubscriptionContextMock(msg, sandbox) - context.stubs.service.OtherService[2].testSubscription.callsFake(async (payload, parameter) => { - return { - result: { - payload: { ...payload, other: 'added by invoke' }, - parameter, - toBeRemovedInResponse: 'removed by output schema', - }, - } - }) - - const result = await subscriptionFunction(context.mock, payload, parameter) - - expect(result).toStrictEqual({ - result: { - payload: { ...payload, other: 'added by invoke', def: 'default_value' }, - parameter: { ...parameter, def: 'default_param' }, - }, - }) - - expect(context.stubs.emit['some'].called).toBeTruthy() - }) - - it('does not throw on transform input', async () => { - const fn = builder.getTransformInputFunction() - - if (!fn) { - expect(fn).toBeDefined() - return - } - - const transformFunction = safeBind(fn, service) - - const msg = getCommandMessageMock({ - payload: { - payload, - parameter, - }, - }) - - const context = builder.getSubscriptionTransformContextMock(msg, sandbox) - - const result = await transformFunction(context.mock, JSON.stringify(payload), JSON.stringify(parameter)) - - expect(result).toStrictEqual({ payload, parameter }) - }) - - it('does not throw on transform output', async () => { - const fn = builder.getTransformOutputFunction() - - if (!fn) { - expect(fn).toBeDefined() - return - } - - const transformFunction = safeBind(fn, service) - - const msg = getCommandMessageMock({ - payload: { - payload, - parameter, - }, - }) - - const context = builder.getSubscriptionTransformContextMock(msg, sandbox) - - const result = await transformFunction( - context.mock, - { - result: { - payload: { ...payload, other: 'added by invoke', def: 'default_value' }, - parameter: { ...parameter, def: 'default_param' }, - }, - }, - { ...parameter, def: 'default_param' }, - ) - - expect(result).toStrictEqual( - JSON.stringify({ - result: { - payload: { ...payload, other: 'added by invoke', def: 'default_value' }, - parameter: { ...parameter, def: 'default_param' }, - }, - }), - ) - }) - - it('works with without schema', async () => { - const b = new SubscriptionDefinitionBuilder('testCommand', 'a unit test command') - - b.setSubscriptionFunction(async (context, payload, parameter) => { - return { payload, parameter } - }) - - const fn = b.getSubscriptionFunction() - - const theFunction = safeBind(fn, service) - - const msg = getCommandMessageMock({ - payload: { - payload: '', - parameter: {}, - }, - }) - - const context = b.getSubscriptionContextMock(msg, sandbox) - - const result = await theFunction(context.mock, 'y', 'x') - - expect(result).toStrictEqual({ - payload: 'y', - parameter: 'x', - }) - }) + const sandbox = createSandbox() + const service = new Service({ + info: { serviceName: 'TestService', serviceVersion: '1', serviceDescription: 'A service' }, + commandDefinitionList: [], + subscriptionDefinitionList: [], + logger: getLoggerMock(sandbox).mock, + eventBridge: getEventBridgeMock(sandbox).mock, + config: {}, + }) + + const functionPayloadSchema = z.object({ foo: z.string(), bar: z.number(), def: z.string().default('default_value') }) + const functionParameterSchema = z.object({ + paramOne: z.string(), + paramTwo: z.number(), + def: z.string().default('default_param'), + }) + const functionOutputSchema = z.object({ + result: z.object({ + payload: z.object({ foo: z.string(), bar: z.number(), other: z.string(), def: z.string() }), + parameter: z.object({ paramOne: z.string(), paramTwo: z.number(), def: z.string() }), + }), + }) + const transformPayloadSchema = z.string() + const transformParameterSchema = z.string() + const transformOutputSchema = z.string() + + const beforeOneStub = sandbox.stub() + const afterOneStub = sandbox.stub() + + const builder = new SubscriptionDefinitionBuilder('testSubscription', 'a unit test subscription') + .addPayloadSchema(functionPayloadSchema) + .addParameterSchema(functionParameterSchema) + .addOutputSchema('subscriptionEndEmitted', functionOutputSchema) + .setTransformInput(transformPayloadSchema, transformParameterSchema, async (_context, payload, parameter) => { + expect(typeof payload).toBe('string') + expect(typeof parameter).toBe('string') + + const pay: { + foo: string + bar: number + } = JSON.parse(payload) + const param: { + paramOne: string + paramTwo: number + } = JSON.parse(parameter) + + return { + payload: pay, + parameter: param, + } + }) + .setTransformOutput(transformOutputSchema, async (_context, payload, _parameter) => { + const p: Readonly<{ + result: { + payload: { + foo: string + bar: number + def: string + other: string + } + parameter: { + paramOne: string + paramTwo: number + } + } + }> = payload + + return JSON.stringify(p) + }) + .setBeforeGuardHooks({ + beforeOne: async (_context, payload, parameter) => { + const pay: { + foo: string + bar: number + def: string + } = payload + + const param: { + def: string + paramOne: string + paramTwo: number + } = parameter + beforeOneStub(pay, param) + }, + }) + .setAfterGuardHooks({ + afterOne: async (_context, fnOutputPayload, input, parameter) => { + const pay: { + foo: string + bar: number + def: string + } = input + + const param: { + def: string + paramOne: string + paramTwo: number + } = parameter + + const fnRes: { + result: { + payload: { + foo: string + bar: number + def: string + other: string + } + parameter: { + def: string + paramOne: string + paramTwo: number + } + } + } = fnOutputPayload + + afterOneStub(fnRes, pay, param) + }, + }) + .canInvoke( + 'OtherService', + '2', + 'testSubscription', + functionOutputSchema.merge(z.object({ toBeRemovedInResponse: z.string() })), + functionPayloadSchema, + functionParameterSchema, + ) + .canEmit('some', z.object({ example: z.string() })) + .setSubscriptionFunction(async (context, payload, parameter) => { + const result = await context.service.OtherService[2].testSubscription(payload, parameter) + + context.emit('some', { example: 'test' }) + + return result + }) + + const payload = { + foo: 'foo', + bar: 1, + } + const parameter = { + paramOne: 'Parameter 1', + paramTwo: 2, + } + + beforeEach(() => { + sandbox.reset() + }) + + afterAll(() => { + sandbox.restore() + }) + + it('does not throw on subscription function', async () => { + const subscriptionFunction = safeBind(builder.getSubscriptionFunction(), service) + + const msg = getCommandMessageMock({ + payload: { + payload, + parameter, + }, + }) + + const context = builder.getSubscriptionContextMock({ message: msg, sandbox }) + context.stubs.service.OtherService[2].testSubscription.callsFake(async (payload, parameter) => { + return { + result: { + payload: { ...payload, other: 'added by invoke' }, + parameter, + toBeRemovedInResponse: 'removed by output schema', + }, + } + }) + + const result = await subscriptionFunction(context.mock, payload, parameter) + + expect(result).toStrictEqual({ + result: { + payload: { ...payload, other: 'added by invoke', def: 'default_value' }, + parameter: { ...parameter, def: 'default_param' }, + }, + }) + + expect(context.stubs.emit.some.called).toBeTruthy() + }) + + it('does not throw on transform input', async () => { + const fn = builder.getTransformInputFunction() + + if (!fn) { + expect(fn).toBeDefined() + return + } + + const transformFunction = safeBind(fn, service) + + const msg = getCommandMessageMock({ + payload: { + payload, + parameter, + }, + }) + + const context = builder.getSubscriptionTransformContextMock({ message: msg, sandbox }) + + const result = await transformFunction(context.mock, JSON.stringify(payload), JSON.stringify(parameter)) + + expect(result).toStrictEqual({ payload, parameter }) + }) + + it('does not throw on transform output', async () => { + const fn = builder.getTransformOutputFunction() + + if (!fn) { + expect(fn).toBeDefined() + return + } + + const transformFunction = safeBind(fn, service) + + const msg = getCommandMessageMock({ + payload: { + payload, + parameter, + }, + }) + + const context = builder.getSubscriptionTransformContextMock({ message: msg, sandbox }) + + const result = await transformFunction( + context.mock, + { + result: { + payload: { ...payload, other: 'added by invoke', def: 'default_value' }, + parameter: { ...parameter, def: 'default_param' }, + }, + }, + { ...parameter, def: 'default_param' }, + ) + + expect(result).toStrictEqual( + JSON.stringify({ + result: { + payload: { ...payload, other: 'added by invoke', def: 'default_value' }, + parameter: { ...parameter, def: 'default_param' }, + }, + }), + ) + }) + + it('works with without schema', async () => { + const b = new SubscriptionDefinitionBuilder('testCommand', 'a unit test command') + + b.setSubscriptionFunction(async (context, payload, parameter) => { + return { payload, parameter } + }) + + const fn = b.getSubscriptionFunction() + + const theFunction = safeBind(fn, service) + + const msg = getCommandMessageMock({ + payload: { + payload: '', + parameter: {}, + }, + }) + + const context = b.getSubscriptionContextMock({ message: msg, sandbox }) + + const result = await theFunction(context.mock, 'y', 'x') + + expect(result).toStrictEqual({ + payload: 'y', + parameter: 'x', + }) + }) }) diff --git a/packages/core/src/SubscriptionDefinitionBuilder/SubscriptionDefinitionBuilderTypes.ts b/packages/core/src/SubscriptionDefinitionBuilder/SubscriptionDefinitionBuilderTypes.ts new file mode 100644 index 000000000..d2ac0d45c --- /dev/null +++ b/packages/core/src/SubscriptionDefinitionBuilder/SubscriptionDefinitionBuilderTypes.ts @@ -0,0 +1,24 @@ +import type { Schema } from '@typeschema/main' +import type { EmptyObject, InvokeList } from '../core/index.js' + +export type SubscriptionDefinitionBuilderTypes< + PayloadSchema extends Schema = Schema, + ParamsSchema extends Schema = Schema, + OutputSchema extends Schema = Schema, + TransformInputPayloadSchema extends Schema = Schema, + TransformInputParamsSchema extends Schema = Schema, + TransformOutputSchema extends Schema = Schema, + Resources extends Record = EmptyObject, + Invokes extends InvokeList = InvokeList, + EmitList extends Record = Record, +> = { + PayloadSchema: PayloadSchema + ParamsSchema: ParamsSchema + OutputSchema: OutputSchema + TransformInputPayloadSchema: TransformInputPayloadSchema + TransformInputParamsSchema: TransformInputParamsSchema + TransformOutputSchema: TransformOutputSchema + Resources: Resources + Invokes: Invokes + EmitList: EmitList +} diff --git a/packages/core/src/SubscriptionDefinitionBuilder/getSubscriptionFunctionWithValidation.impl.ts b/packages/core/src/SubscriptionDefinitionBuilder/getSubscriptionFunctionWithValidation.impl.ts index 133e07747..355d8a25f 100644 --- a/packages/core/src/SubscriptionDefinitionBuilder/getSubscriptionFunctionWithValidation.impl.ts +++ b/packages/core/src/SubscriptionDefinitionBuilder/getSubscriptionFunctionWithValidation.impl.ts @@ -2,142 +2,121 @@ import { SpanStatusCode } from '@opentelemetry/api' import type { Schema } from '@typeschema/main' import { validate } from '@typeschema/main' -import type { ServiceClass, SubscriptionBeforeGuardHook, SubscriptionFunction } from '../core/index.js' +import type { + Service, + SubscriptionBeforeGuardHook, + SubscriptionFunction, + SubscriptionFunctionContext, +} from '../core/index.js' import { HandledError, StatusCode, UnhandledError } from '../core/index.js' -export const getSubscriptionFunctionWithValidation = function < - ServiceClassType extends ServiceClass, - MessagePayloadType = unknown, - MessageParamsType = unknown, - MessageResultType = unknown, - FunctionPayloadType = MessagePayloadType, - FunctionParamsType = MessageParamsType, - FunctionResultType = MessageResultType, - Invokes = {}, - EmitListType = {}, ->( - fn: SubscriptionFunction< - ServiceClassType, - MessagePayloadType, - MessageParamsType, - FunctionPayloadType, - FunctionParamsType, - FunctionResultType, - Invokes, - EmitListType - >, - inputPayloadSchema: Schema | undefined, - inputParameterSchema: Schema | undefined, - outputPayloadSchema: Schema | undefined, - beforeGuards: Record< - string, - SubscriptionBeforeGuardHook - > = {}, -): SubscriptionFunction< - ServiceClassType, - MessagePayloadType, - MessageParamsType, - FunctionPayloadType, - FunctionParamsType, - FunctionResultType, - Invokes, - EmitListType -> { - const wrapped: SubscriptionFunction< - ServiceClassType, - MessagePayloadType, - MessageParamsType, - FunctionPayloadType, - FunctionParamsType, - FunctionResultType, - Invokes, - EmitListType - > = async function (context, payload, parameter): Promise { - const { logger, startActiveSpan, wrapInSpan } = context - let safePayload = payload as unknown as FunctionPayloadType - if (inputPayloadSchema) { - safePayload = await startActiveSpan('validatePayload', {}, undefined, async (span) => { - const validationResult = await validate(inputPayloadSchema, payload) - if (validationResult.success) { - return validationResult.data as FunctionPayloadType - } - const err = new HandledError( - StatusCode.BadRequest, - 'input validation for payload failed', - validationResult.issues, - ) - span.recordException(err) - span.setStatus({ - code: SpanStatusCode.ERROR, - message: err.message, - }) - logger.warn({ ...span.spanContext() }, 'input validation for payload failed:', err.message) - throw err - }) - } +export const getSubscriptionFunctionWithValidation = function ( + fn: SubscriptionFunction, + inputPayloadSchema: Schema | undefined, + inputParameterSchema: Schema | undefined, + outputPayloadSchema: Schema | undefined, + beforeGuards: Record> = {}, +) { + const wrapped = async function ( + this: S, + context: SubscriptionFunctionContext, + payload: unknown, + parameter: unknown, + ): Promise { + const { logger, startActiveSpan, wrapInSpan } = context - let safeParams = parameter as unknown as FunctionParamsType - if (inputParameterSchema) { - safeParams = await startActiveSpan('validateParameter', {}, undefined, async (span) => { - const validationResult = await validate(inputParameterSchema, parameter) - if (validationResult.success) { - return validationResult.data as FunctionParamsType - } + const getPayloadValue = async (): Promise => { + if (!inputPayloadSchema) { + return payload + } - const err = new HandledError( - StatusCode.BadRequest, - 'input validation for parameter failed', - validationResult.issues, - ) - span.recordException(err) - span.setStatus({ - code: SpanStatusCode.ERROR, - message: err.message, - }) - logger.warn({ ...span.spanContext() }, 'input validation for parameter failed:', err.message) - throw err - }) - } + return await startActiveSpan('validatePayload', {}, undefined, async span => { + const validationResult = await validate(inputPayloadSchema, payload) + if (validationResult.success) { + return validationResult.data + } + const err = new HandledError( + StatusCode.BadRequest, + 'input validation for payload failed', + validationResult.issues, + ) + span.recordException(err) + span.setStatus({ + code: SpanStatusCode.ERROR, + message: err.message, + }) + logger.warn({ ...span.spanContext() }, 'input validation for payload failed:', err.message) + throw err + }) + } - if (Object.keys(beforeGuards).length) { - await startActiveSpan('beforeGuardHooks', {}, undefined, async () => { - const guards: Promise[] = [] + const getParameterValue = async (): Promise => { + if (!inputParameterSchema) { + return parameter + } - for (const [name, hook] of Object.entries(beforeGuards)) { - const guardPromise = wrapInSpan('beforeGuardHook.' + name, {}, async (_subSpan) => { - return hook.bind(this, context, safePayload, safeParams)() - }) - guards.push(guardPromise) - } + return startActiveSpan('validateParameter', {}, undefined, async span => { + const validationResult = await validate(inputParameterSchema, parameter) + if (validationResult.success) { + return validationResult.data + } - await Promise.all(guards) - }) - } + const err = new HandledError( + StatusCode.BadRequest, + 'input validation for parameter failed', + validationResult.issues, + ) + span.recordException(err) + span.setStatus({ + code: SpanStatusCode.ERROR, + message: err.message, + }) + logger.warn({ ...span.spanContext() }, 'input validation for parameter failed:', err.message) + throw err + }) + } - const output = await startActiveSpan('functionExecution', {}, undefined, async () => { - const call = fn.bind(this, context, safePayload, safeParams) - return call() - }) + const [safePayload, safeParams] = await Promise.all([getPayloadValue(), getParameterValue()]) - if (!outputPayloadSchema) { - return output - } + if (Object.keys(beforeGuards).length) { + await startActiveSpan('beforeGuardHooks', {}, undefined, async () => { + const guards: Promise[] = [] - return await startActiveSpan('outputValidation', {}, undefined, async (span) => { - const validationResult = await validate(outputPayloadSchema, output) - if (validationResult.success) { - return validationResult.data as FunctionResultType - } + for (const [name, hook] of Object.entries(beforeGuards)) { + const guardPromise = wrapInSpan(`beforeGuardHook.${name}`, {}, async _subSpan => { + return hook.bind(this, context, safePayload, safeParams)() + }) + guards.push(guardPromise) + } - const err = new UnhandledError(StatusCode.InternalServerError, 'output validation failed') - span.recordException(err) - span.setStatus({ - code: SpanStatusCode.ERROR, - message: err.message, - }) - logger.warn({ ...span.spanContext() }, 'output validation failed:', err.message) - throw err - }) - } - return wrapped + await Promise.all(guards) + }) + } + + const output = await startActiveSpan('functionExecution', {}, undefined, async () => { + const call = fn.bind(this, context, safePayload, safeParams) + return call() + }) + + if (!outputPayloadSchema) { + return output + } + + return await startActiveSpan('outputValidation', {}, undefined, async span => { + const validationResult = await validate(outputPayloadSchema, output) + if (validationResult.success) { + return validationResult.data + } + + const err = new UnhandledError(StatusCode.InternalServerError, 'output validation failed') + span.recordException(err) + span.setStatus({ + code: SpanStatusCode.ERROR, + message: err.message, + }) + logger.warn({ ...span.spanContext() }, 'output validation failed:', err.message) + throw err + }) + } + return wrapped } diff --git a/packages/core/src/SubscriptionDefinitionBuilder/getSubscriptionFunctionWithValidation.test.ts b/packages/core/src/SubscriptionDefinitionBuilder/getSubscriptionFunctionWithValidation.test.ts index 325768896..6ffab2dcb 100644 --- a/packages/core/src/SubscriptionDefinitionBuilder/getSubscriptionFunctionWithValidation.test.ts +++ b/packages/core/src/SubscriptionDefinitionBuilder/getSubscriptionFunctionWithValidation.test.ts @@ -1,6 +1,6 @@ describe('getSubscriptionFunctionWithValidation', () => { - it.todo('throws if parameter schema validation fails') - it.todo('throws if input schema validation fails') - it.todo('returns output if no schema is defined') - it.todo('throws if output schema validation fails') + it.todo('throws if parameter schema validation fails') + it.todo('throws if input schema validation fails') + it.todo('returns output if no schema is defined') + it.todo('throws if output schema validation fails') }) diff --git a/packages/core/src/SubscriptionDefinitionBuilder/index.ts b/packages/core/src/SubscriptionDefinitionBuilder/index.ts index 429c0203f..fe5089fc9 100644 --- a/packages/core/src/SubscriptionDefinitionBuilder/index.ts +++ b/packages/core/src/SubscriptionDefinitionBuilder/index.ts @@ -1,2 +1,3 @@ export * from './getSubscriptionFunctionWithValidation.impl.js' export * from './SubscriptionDefinitionBuilder.impl.js' +export * from './SubscriptionDefinitionBuilderTypes.js' diff --git a/packages/core/src/core/ConfigStore/ConfigStoreBaseClass.impl.ts b/packages/core/src/core/ConfigStore/ConfigStoreBaseClass.impl.ts index 6649e5623..be371fe3a 100644 --- a/packages/core/src/core/ConfigStore/ConfigStoreBaseClass.impl.ts +++ b/packages/core/src/core/ConfigStore/ConfigStoreBaseClass.impl.ts @@ -1,7 +1,7 @@ import { initLogger } from '../../DefaultLogger/index.js' import type { ObjectWithKeysFromStringArray } from '../../helper/index.js' import { UnhandledError } from '../Error/index.js' -import type { Logger, StoreBaseConfig } from '../types/index.js' +import type { EmptyObject, Logger, StoreBaseConfig } from '../types/index.js' import { StatusCode } from '../types/index.js' import type { ConfigStoreCacheMap } from './types/index.js' @@ -17,113 +17,113 @@ import type { ConfigStoreCacheMap } from './types/index.js' * * @group Store */ -export abstract class ConfigStoreBaseClass = {}> { - logger: Logger - config: StoreBaseConfig +export abstract class ConfigStoreBaseClass = EmptyObject> { + logger: Logger + config: StoreBaseConfig - name: string + name: string - cache: ConfigStoreCacheMap = new Map() + cache: ConfigStoreCacheMap = new Map() - constructor(name: string, config: StoreBaseConfig) { - const logger = config?.logger ?? initLogger(config?.logLevel) - this.logger = logger.getChildLogger({ name }) + constructor(name: string, config: StoreBaseConfig) { + const logger = config?.logger ?? initLogger(config?.logLevel) + this.logger = logger.getChildLogger({ name }) - this.name = name + this.name = name - this.config = { - enableGet: true, - enableSet: false, - enableRemove: false, - enableCache: false, - ...config, - } - } + this.config = { + enableGet: true, + enableSet: false, + enableRemove: false, + enableCache: false, + ...config, + } + } - /** - * This method must be overwritten by actual store implementation. - * - * @param configNames list of config items - * @returns an object of { [configName]: value | undefined } - */ - protected abstract getConfigImpl( - // eslint-disable-next-line @typescript-eslint/no-unused-vars - ...configNames: ConfigNames - ): Promise> + /** + * This method must be overwritten by actual store implementation. + * + * @param configNames list of config items + * @returns an object of { [configName]: value | undefined } + */ + protected abstract getConfigImpl( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + ...configNames: ConfigNames + ): Promise> - /** - * Returns the values for given config properties. - * This function **SHOULD NOT** be overwritten by store implementation. - * For implementation overwrite protected `getConfigImpl` - * - * @param configNames - * @returns an object of { [configName]: value | undefined } - */ - async getConfig( - ...configNames: ConfigNames - ): Promise> { - if (!this.config.enableGet) { - const err = new UnhandledError(StatusCode.Unauthorized, 'get config from store is disabled by config') - this.logger.error({ err }, err.message) - throw err - } - return this.getConfigImpl(...configNames) - } + /** + * Returns the values for given config properties. + * This function **SHOULD NOT** be overwritten by store implementation. + * For implementation overwrite protected `getConfigImpl` + * + * @param configNames + * @returns an object of { [configName]: value | undefined } + */ + async getConfig( + ...configNames: ConfigNames + ): Promise> { + if (!this.config.enableGet) { + const err = new UnhandledError(StatusCode.Unauthorized, 'get config from store is disabled by config') + this.logger.error({ err }, err.message) + throw err + } + return this.getConfigImpl(...configNames) + } - /** - * This method must be overwritten by actual store implementation. - * - * @param configName - */ - // eslint-disable-next-line @typescript-eslint/no-unused-vars - protected abstract removeConfigImpl(configName: string): Promise + /** + * This method must be overwritten by actual store implementation. + * + * @param configName + */ + // eslint-disable-next-line @typescript-eslint/no-unused-vars + protected abstract removeConfigImpl(configName: string): Promise - /** - * Removes the config item given by config name. - * This function **SHOULD NOT** be overwritten by store implementation. - * For implementation overwrite protected `removeConfigImpl` - * - * @param configName - * @returns - */ - async removeConfig(configName: string): Promise { - if (!this.config.enableRemove) { - const err = new UnhandledError(StatusCode.Unauthorized, 'remove config from store is disabled by config') - this.logger.error({ err }, err.message) - throw err - } + /** + * Removes the config item given by config name. + * This function **SHOULD NOT** be overwritten by store implementation. + * For implementation overwrite protected `removeConfigImpl` + * + * @param configName + * @returns + */ + async removeConfig(configName: string): Promise { + if (!this.config.enableRemove) { + const err = new UnhandledError(StatusCode.Unauthorized, 'remove config from store is disabled by config') + this.logger.error({ err }, err.message) + throw err + } - return this.removeConfigImpl(configName) - } + return this.removeConfigImpl(configName) + } - /** - * This method must be overwritten by actual store implementation. - * - * @param _configName - * @param _configValue - */ - protected abstract setConfigImpl(_configName: string, _configValue: unknown): Promise + /** + * This method must be overwritten by actual store implementation. + * + * @param _configName + * @param _configValue + */ + protected abstract setConfigImpl(_configName: string, _configValue: unknown): Promise - /** - * Sets a config value - * This function **SHOULD NOT** be overwritten by store implementation. - * For implementation overwrite protected `setConfigImpl` - * - * @param configName - * @param configValue - * @returns - */ - async setConfig(configName: string, configValue: unknown) { - if (!this.config.enableSet) { - const err = new UnhandledError(StatusCode.Unauthorized, 'set config at store is disabled by config') - this.logger.error({ err }, err.message) - throw err - } + /** + * Sets a config value + * This function **SHOULD NOT** be overwritten by store implementation. + * For implementation overwrite protected `setConfigImpl` + * + * @param configName + * @param configValue + * @returns + */ + async setConfig(configName: string, configValue: unknown) { + if (!this.config.enableSet) { + const err = new UnhandledError(StatusCode.Unauthorized, 'set config at store is disabled by config') + this.logger.error({ err }, err.message) + throw err + } - return this.setConfigImpl(configName, configValue) - } + return this.setConfigImpl(configName, configValue) + } - async destroy() { - this.logger.info('stopped') - } + async destroy() { + this.logger.info('stopped') + } } diff --git a/packages/core/src/core/ConfigStore/configStoreBaseClass.test.ts b/packages/core/src/core/ConfigStore/configStoreBaseClass.test.ts index d153e4212..0588758c7 100644 --- a/packages/core/src/core/ConfigStore/configStoreBaseClass.test.ts +++ b/packages/core/src/core/ConfigStore/configStoreBaseClass.test.ts @@ -8,88 +8,88 @@ import { StatusCode } from '../types/index.js' import { ConfigStoreBaseClass } from './ConfigStoreBaseClass.impl.js' class TestClass extends ConfigStoreBaseClass { - protected getConfigImpl( - ..._configNames: ConfigNames - ): Promise> { - throw new Error('Not implemented') - } - - protected setConfigImpl(_configName: string, _configValue: unknown): Promise { - throw new Error('Not implemented') - } - - protected removeConfigImpl(_configName: string): Promise { - throw new Error('Not implemented') - } + protected getConfigImpl( + ..._configNames: ConfigNames + ): Promise> { + throw new Error('Not implemented') + } + + protected setConfigImpl(_configName: string, _configValue: unknown): Promise { + throw new Error('Not implemented') + } + + protected removeConfigImpl(_configName: string): Promise { + throw new Error('Not implemented') + } } describe('ConfigStoreBaseClass', () => { - let sandbox: SinonSandbox - let configStore: ConfigStoreBaseClass - let logger: ReturnType + let sandbox: SinonSandbox + let configStore: ConfigStoreBaseClass + let logger: ReturnType - beforeEach(() => { - sandbox = createSandbox() - logger = getLoggerMock(sandbox) - configStore = new TestClass('test', { logger: logger.mock }) - }) + beforeEach(() => { + sandbox = createSandbox() + logger = getLoggerMock(sandbox) + configStore = new TestClass('test', { logger: logger.mock }) + }) - afterEach(() => { - sandbox.restore() - }) + afterEach(() => { + sandbox.restore() + }) - describe('getConfig', () => { - it('should throw an UnhandledError if enableGet is false', async () => { - sandbox.stub(configStore.config, 'enableGet').value(false) + describe('getConfig', () => { + it('should throw an UnhandledError if enableGet is false', async () => { + sandbox.stub(configStore.config, 'enableGet').value(false) - await expect(configStore.getConfig('test')).rejects.toEqual( - new UnhandledError(StatusCode.Unauthorized, 'get config from store is disabled by config'), - ) + await expect(configStore.getConfig('test')).rejects.toEqual( + new UnhandledError(StatusCode.Unauthorized, 'get config from store is disabled by config'), + ) - sandbox.assert.calledOnce(logger.stubs.error) - }) + sandbox.assert.calledOnce(logger.stubs.error) + }) - it('should throw an UnhandledError if enableGet is true but method is not implemented', async () => { - sandbox.stub(configStore.config, 'enableGet').value(true) + it('should throw an UnhandledError if enableGet is true but method is not implemented', async () => { + sandbox.stub(configStore.config, 'enableGet').value(true) - await expect(configStore.getConfig('test')).rejects.toEqual(new Error('Not implemented')) - }) - }) + await expect(configStore.getConfig('test')).rejects.toEqual(new Error('Not implemented')) + }) + }) - describe('setConfig', () => { - it('should throw an UnhandledError if enableSet is false', async () => { - sandbox.stub(configStore.config, 'enableSet').value(false) + describe('setConfig', () => { + it('should throw an UnhandledError if enableSet is false', async () => { + sandbox.stub(configStore.config, 'enableSet').value(false) - await expect(configStore.setConfig('test', {})).rejects.toEqual( - new UnhandledError(StatusCode.Unauthorized, 'set config at store is disabled by config'), - ) + await expect(configStore.setConfig('test', {})).rejects.toEqual( + new UnhandledError(StatusCode.Unauthorized, 'set config at store is disabled by config'), + ) - sandbox.assert.calledOnce(logger.stubs.error) - }) + sandbox.assert.calledOnce(logger.stubs.error) + }) - it('should throw an UnhandledError if enableSet is true but method is not implemented', async () => { - // Arrange - sandbox.stub(configStore.config, 'enableSet').value(true) + it('should throw an UnhandledError if enableSet is true but method is not implemented', async () => { + // Arrange + sandbox.stub(configStore.config, 'enableSet').value(true) - await expect(configStore.setConfig('test', {})).rejects.toEqual(new Error('Not implemented')) - }) - }) + await expect(configStore.setConfig('test', {})).rejects.toEqual(new Error('Not implemented')) + }) + }) - describe('removeConfig', () => { - it('should throw an UnhandledError if enableRemove is false', async () => { - sandbox.stub(configStore.config, 'enableRemove').value(false) + describe('removeConfig', () => { + it('should throw an UnhandledError if enableRemove is false', async () => { + sandbox.stub(configStore.config, 'enableRemove').value(false) - await expect(configStore.removeConfig('test')).rejects.toMatchObject( - new UnhandledError(StatusCode.Unauthorized, 'remove config from store is disabled by config'), - ) + await expect(configStore.removeConfig('test')).rejects.toMatchObject( + new UnhandledError(StatusCode.Unauthorized, 'remove config from store is disabled by config'), + ) - sandbox.assert.calledOnce(logger.stubs.error) - }) + sandbox.assert.calledOnce(logger.stubs.error) + }) - it('should throw an UnhandledError if enableRemove is true but method is not implemented', async () => { - sandbox.stub(configStore.config, 'enableRemove').value(true) + it('should throw an UnhandledError if enableRemove is true but method is not implemented', async () => { + sandbox.stub(configStore.config, 'enableRemove').value(true) - await expect(configStore.removeConfig('test')).rejects.toMatchObject(new Error('Not implemented')) - }) - }) + await expect(configStore.removeConfig('test')).rejects.toMatchObject(new Error('Not implemented')) + }) + }) }) diff --git a/packages/core/src/core/ConfigStore/types/ConfigGetterFunction.ts b/packages/core/src/core/ConfigStore/types/ConfigGetterFunction.ts index ee2f4304f..e28e15021 100644 --- a/packages/core/src/core/ConfigStore/types/ConfigGetterFunction.ts +++ b/packages/core/src/core/ConfigStore/types/ConfigGetterFunction.ts @@ -2,5 +2,5 @@ import type { ObjectWithKeysFromStringArray } from '../../../helper/types/Object /** get a config value from the config store @group Store */ export type ConfigGetterFunction = ( - ...configNames: ConfigNames + ...configNames: ConfigNames ) => Promise> diff --git a/packages/core/src/core/ConfigStore/types/ConfigStore.ts b/packages/core/src/core/ConfigStore/types/ConfigStore.ts index a9c4f8d0a..529166571 100644 --- a/packages/core/src/core/ConfigStore/types/ConfigStore.ts +++ b/packages/core/src/core/ConfigStore/types/ConfigStore.ts @@ -8,33 +8,33 @@ import type { ConfigSetterFunction } from './ConfigSetterFunction.js' * @group Store */ export interface ConfigStore { - /** name of store */ - name: string - /** - * get a config value - * @param string name of config - * @returns the config - * @throws UnhandledError - */ - getConfig: ConfigGetterFunction + /** name of store */ + name: string + /** + * get a config value + * @param string name of config + * @returns the config + * @throws UnhandledError + */ + getConfig: ConfigGetterFunction - /** - * set a config value - * @param string name of config - * @param value value of config - * @throws UnhandledError - */ - setConfig: ConfigSetterFunction + /** + * set a config value + * @param string name of config + * @param value value of config + * @throws UnhandledError + */ + setConfig: ConfigSetterFunction - /** - * delete a config value - * @param string name of config - * @throws UnhandledError - */ - removeConfig: ConfigDeleteFunction + /** + * delete a config value + * @param string name of config + * @throws UnhandledError + */ + removeConfig: ConfigDeleteFunction - /** - * disconnects and shuts down the config store - */ - destroy(): Promise + /** + * disconnects and shuts down the config store + */ + destroy(): Promise } diff --git a/packages/core/src/core/Error/HandledError.impl.ts b/packages/core/src/core/Error/HandledError.impl.ts index 0ce93c45a..6b3b4c9ee 100644 --- a/packages/core/src/core/Error/HandledError.impl.ts +++ b/packages/core/src/core/Error/HandledError.impl.ts @@ -9,79 +9,79 @@ import { UnhandledError } from './UnhandledError.impl.js' * Scenarios are input validation failures or "404 Not Found" errors which should be returned to the caller. */ export class HandledError extends Error { - constructor( - public errorCode: StatusCode, - message?: string, - public data?: unknown, - public traceId?: TraceId, - ) { - /* Calling the constructor of the parent class (Error) and passing the message. */ - super(message ?? getErrorMessageForCode(errorCode)) - Error.captureStackTrace(this, this.constructor) + constructor( + public errorCode: StatusCode, + message?: string, + public data?: unknown, + public traceId?: TraceId, + ) { + /* Calling the constructor of the parent class (Error) and passing the message. */ + super(message ?? getErrorMessageForCode(errorCode)) + Error.captureStackTrace(this, this.constructor) - Object.setPrototypeOf(this, HandledError.prototype) - this.name = this.constructor.name - } + Object.setPrototypeOf(this, HandledError.prototype) + this.name = this.constructor.name + } - /** - * Create a error object from EBMessage error message - * @param message CommandErrorResponse - * @returns HandledError - */ - static fromMessage(message: Readonly): HandledError { - return new HandledError(message.payload.status, message.payload.message, message.payload.data, message.traceId) - } + /** + * Create a error object from EBMessage error message + * @param message CommandErrorResponse + * @returns HandledError + */ + static fromMessage(message: Readonly): HandledError { + return new HandledError(message.payload.status, message.payload.message, message.payload.data, message.traceId) + } - /** - * Creates a HandledError from an input. - * If the input error is a HandledError it will be returned without modifications. - * - * @param err the input - * @param errorCode the error code - * @param data optional data - * @param traceId optional trace id - * @returns HandledError - */ - static fromError(err: any, errorCode?: StatusCode, data?: unknown, traceId?: TraceId): HandledError { - if (err instanceof HandledError) { - return err - } + /** + * Creates a HandledError from an input. + * If the input error is a HandledError it will be returned without modifications. + * + * @param err the input + * @param errorCode the error code + * @param data optional data + * @param traceId optional trace id + * @returns HandledError + */ + static fromError(err: any, errorCode?: StatusCode, data?: unknown, traceId?: TraceId): HandledError { + if (err instanceof HandledError) { + return err + } - let t - if (err instanceof HandledError || err instanceof UnhandledError) { - t = err.traceId - } + let t: string | undefined + if (err instanceof HandledError || err instanceof UnhandledError) { + t = err.traceId + } - const error = new HandledError(errorCode ?? StatusCode.InternalServerError, err.message, data, traceId ?? t) - error.stack = err.stack - error.cause = err.cause - return error - } + const error = new HandledError(errorCode ?? StatusCode.InternalServerError, err.message, data, traceId ?? t) + error.stack = err.stack + error.cause = err.cause + return error + } - /** - * Returns error response object - * @returns ErrorResponsePayload - */ - getErrorResponse(traceId?: TraceId) { - const errorResponse: Readonly = Object.freeze({ - status: this.errorCode, - message: this.message, - data: this.data, - traceId: this.traceId ?? traceId, - }) + /** + * Returns error response object + * @returns ErrorResponsePayload + */ + getErrorResponse(traceId?: TraceId) { + const errorResponse: Readonly = Object.freeze({ + status: this.errorCode, + message: this.message, + data: this.data, + traceId: this.traceId ?? traceId, + }) - return errorResponse - } + return errorResponse + } - /** - * Returns stringified error response object - * @returns ErrorResponse as string - */ - toString() { - return JSON.stringify(this.getErrorResponse()) - } + /** + * Returns stringified error response object + * @returns ErrorResponse as string + */ + toString() { + return JSON.stringify(this.getErrorResponse()) + } - toJSON() { - return { stack: this.stack, name: this.name, ...this.getErrorResponse() } - } + toJSON() { + return { stack: this.stack, name: this.name, ...this.getErrorResponse() } + } } diff --git a/packages/core/src/core/Error/UnhandledError.impl.ts b/packages/core/src/core/Error/UnhandledError.impl.ts index a4648cd36..02c6f8b34 100644 --- a/packages/core/src/core/Error/UnhandledError.impl.ts +++ b/packages/core/src/core/Error/UnhandledError.impl.ts @@ -11,81 +11,81 @@ import { HandledError } from './HandledError.impl.js' * Unhandled error are automatically converted into "500 Internal Server Error" to the outside world. */ export class UnhandledError extends Error { - constructor( - public errorCode: StatusCode = StatusCode.InternalServerError, - message?: string, - public data?: unknown, - public traceId?: TraceId, - ) { - super(message ?? getErrorMessageForCode(errorCode)) - Error.captureStackTrace(this, this.constructor) + constructor( + public errorCode: StatusCode = StatusCode.InternalServerError, + message?: string, + public data?: unknown, + public traceId?: TraceId, + ) { + super(message ?? getErrorMessageForCode(errorCode)) + Error.captureStackTrace(this, this.constructor) - Object.setPrototypeOf(this, UnhandledError.prototype) + Object.setPrototypeOf(this, UnhandledError.prototype) - this.name = this.constructor.name - } + this.name = this.constructor.name + } - /** - * Create a error object from EBMessage error message - * @param message CommandErrorResponse - * @returns UnhandledError - */ - static fromMessage(message: Readonly): UnhandledError { - return new UnhandledError(message.payload.status, message.payload.message, message.payload.data, message.traceId) - } + /** + * Create a error object from EBMessage error message + * @param message CommandErrorResponse + * @returns UnhandledError + */ + static fromMessage(message: Readonly): UnhandledError { + return new UnhandledError(message.payload.status, message.payload.message, message.payload.data, message.traceId) + } - /** - * Creates a UnhandledError from an input - * - * @param err the input - * @param errorCode the error code - * @param data optional data - * @param traceId optional trace id - * @returns UnhandledError - */ - static fromError(err: any, errorCode?: StatusCode, data?: unknown, traceId?: TraceId): HandledError { - let t - if (err instanceof HandledError || err instanceof UnhandledError) { - t = err.traceId - } - const error = new UnhandledError(errorCode ?? StatusCode.InternalServerError, err.message, data, traceId ?? t) - error.stack = err.stack - error.cause = err.cause - return error - } + /** + * Creates a UnhandledError from an input + * + * @param err the input + * @param errorCode the error code + * @param data optional data + * @param traceId optional trace id + * @returns UnhandledError + */ + static fromError(err: any, errorCode?: StatusCode, data?: unknown, traceId?: TraceId): HandledError { + let t: string | undefined + if (err instanceof HandledError || err instanceof UnhandledError) { + t = err.traceId + } + const error = new UnhandledError(errorCode ?? StatusCode.InternalServerError, err.message, data, traceId ?? t) + error.stack = err.stack + error.cause = err.cause + return error + } - /** - * Create a handled error from unhandled error - * @returns HandledError - */ - intoHandledError(): HandledError { - return new HandledError(this.errorCode, this.message, this.data, this.traceId) - } + /** + * Create a handled error from unhandled error + * @returns HandledError + */ + intoHandledError(): HandledError { + return new HandledError(this.errorCode, this.message, this.data, this.traceId) + } - /** - * Returns error response object - * @returns ErrorResponsePayload - */ - getErrorResponse() { - const errorResponse: Readonly = Object.freeze({ - status: this.errorCode, - message: this.message, - data: this.data, - traceId: this.traceId, - }) + /** + * Returns error response object + * @returns ErrorResponsePayload + */ + getErrorResponse() { + const errorResponse: Readonly = Object.freeze({ + status: this.errorCode, + message: this.message, + data: this.data, + traceId: this.traceId, + }) - return errorResponse - } + return errorResponse + } - /** - * Returns stringified error response object - * @returns ErrorResponse as string - */ - toString() { - return JSON.stringify(this.getErrorResponse()) - } + /** + * Returns stringified error response object + * @returns ErrorResponse as string + */ + toString() { + return JSON.stringify(this.getErrorResponse()) + } - toJSON() { - return { stack: this.stack, name: this.name, ...this.getErrorResponse() } - } + toJSON() { + return { stack: this.stack, name: this.name, ...this.getErrorResponse() } + } } diff --git a/packages/core/src/core/Error/handledError.test.ts b/packages/core/src/core/Error/handledError.test.ts index 57a779ffe..4feb021f0 100644 --- a/packages/core/src/core/Error/handledError.test.ts +++ b/packages/core/src/core/Error/handledError.test.ts @@ -3,85 +3,85 @@ import { EBMessageType, StatusCode } from '../types/index.js' import { HandledError } from './HandledError.impl.js' describe('HandledError', () => { - const sender = { - serviceName: 'SenderService', - serviceVersion: '1.1.1', - serviceTarget: 'senderServiceTarget', - instanceId: 'a', - } + const sender = { + serviceName: 'SenderService', + serviceVersion: '1.1.1', + serviceTarget: 'senderServiceTarget', + instanceId: 'a', + } - const receiver = { - serviceName: 'ReceiverService', - serviceVersion: '2.2.2', - serviceTarget: 'receiverServiceTarget', - instanceId: 'a', - } + const receiver = { + serviceName: 'ReceiverService', + serviceVersion: '2.2.2', + serviceTarget: 'receiverServiceTarget', + instanceId: 'a', + } - it('creates a new HandledError', () => { - const statusCode = StatusCode.BadRequest - const message = 'invalid input' - const data = { some: 'data' } - const traceId = 'messageTraceId' + it('creates a new HandledError', () => { + const statusCode = StatusCode.BadRequest + const message = 'invalid input' + const data = { some: 'data' } + const traceId = 'messageTraceId' - const error = new HandledError(statusCode, message, data, traceId) + const error = new HandledError(statusCode, message, data, traceId) - const response = error.getErrorResponse() + const response = error.getErrorResponse() - expect(response.data).toEqual(data) - expect(response.message).toEqual(message) - expect(response.status).toEqual(statusCode) - expect(response.traceId).toEqual(traceId) + expect(response.data).toEqual(data) + expect(response.message).toEqual(message) + expect(response.status).toEqual(statusCode) + expect(response.traceId).toEqual(traceId) - const strResponse = error.toString() - const result = JSON.parse(strResponse) + const strResponse = error.toString() + const result = JSON.parse(strResponse) - expect(result.data).toEqual(data) - expect(result.message).toEqual(message) - expect(result.status).toEqual(statusCode) - expect(result.traceId).toEqual(traceId) - }) + expect(result.data).toEqual(data) + expect(result.message).toEqual(message) + expect(result.status).toEqual(statusCode) + expect(result.traceId).toEqual(traceId) + }) - it('creates a HandledError from error', () => { - const statusCode = StatusCode.BadRequest - const message = 'invalid input' - const data = { some: 'data' } - const traceId = 'messageTraceId' + it('creates a HandledError from error', () => { + const statusCode = StatusCode.BadRequest + const message = 'invalid input' + const data = { some: 'data' } + const traceId = 'messageTraceId' - const commandErrorResponse: CommandErrorResponse = { - messageType: EBMessageType.CommandErrorResponse, - isHandledError: true, - id: 'messageTestId', - traceId, - timestamp: Date.now(), - correlationId: 'messageCorrelationId', - principalId: 'messagePrincipalId', - tenantId: 'messageTenantId', - contentType: 'application/json', - contentEncoding: 'utf-8', - sender, - receiver, - payload: { - status: statusCode, - message, - data, - }, - } + const commandErrorResponse: CommandErrorResponse = { + messageType: EBMessageType.CommandErrorResponse, + isHandledError: true, + id: 'messageTestId', + traceId, + timestamp: Date.now(), + correlationId: 'messageCorrelationId', + principalId: 'messagePrincipalId', + tenantId: 'messageTenantId', + contentType: 'application/json', + contentEncoding: 'utf-8', + sender, + receiver, + payload: { + status: statusCode, + message, + data, + }, + } - const error = HandledError.fromMessage(commandErrorResponse) + const error = HandledError.fromMessage(commandErrorResponse) - const response = error.getErrorResponse() + const response = error.getErrorResponse() - expect(response.data).toEqual(data) - expect(response.message).toEqual(message) - expect(response.status).toEqual(statusCode) - expect(response.traceId).toEqual(traceId) + expect(response.data).toEqual(data) + expect(response.message).toEqual(message) + expect(response.status).toEqual(statusCode) + expect(response.traceId).toEqual(traceId) - const strResponse = error.toString() - const result = JSON.parse(strResponse) + const strResponse = error.toString() + const result = JSON.parse(strResponse) - expect(result.data).toEqual(data) - expect(result.message).toEqual(message) - expect(result.status).toEqual(statusCode) - expect(result.traceId).toEqual(traceId) - }) + expect(result.data).toEqual(data) + expect(result.message).toEqual(message) + expect(result.status).toEqual(statusCode) + expect(result.traceId).toEqual(traceId) + }) }) diff --git a/packages/core/src/core/Error/unhandledError.test.ts b/packages/core/src/core/Error/unhandledError.test.ts index 9dbbb2736..95bb20a83 100644 --- a/packages/core/src/core/Error/unhandledError.test.ts +++ b/packages/core/src/core/Error/unhandledError.test.ts @@ -4,102 +4,102 @@ import { HandledError } from './HandledError.impl.js' import { UnhandledError } from './UnhandledError.impl.js' describe('UnhandledError', () => { - const sender = { - serviceName: 'SenderService', - serviceVersion: '1.1.1', - serviceTarget: 'senderServiceTarget', - instanceId: 'a', - } - - const receiver = { - serviceName: 'ReceiverService', - serviceVersion: '2.2.2', - serviceTarget: 'receiverServiceTarget', - instanceId: 'a', - } - - it('creates a new UnhandledError', () => { - const statusCode = StatusCode.BadRequest - const message = 'invalid input' - const data = { some: 'data' } - const traceId = 'messageTraceId' - - const error = new UnhandledError(statusCode, message, data, traceId) - - const response = error.getErrorResponse() - - expect(response.data).toEqual(data) - expect(response.message).toEqual(message) - expect(response.status).toEqual(statusCode) - expect(response.traceId).toEqual(traceId) - - const strResponse = error.toString() - const result = JSON.parse(strResponse) - - expect(result.data).toEqual(data) - expect(result.message).toEqual(message) - expect(result.status).toEqual(statusCode) - expect(result.traceId).toEqual(traceId) - }) - - it('creates a UnhandledError from error', () => { - const statusCode = StatusCode.BadRequest - const message = 'invalid input' - const data = { some: 'data' } - const traceId = 'messageTraceId' - - const commandErrorResponse: CommandErrorResponse = { - messageType: EBMessageType.CommandErrorResponse, - isHandledError: false, - id: 'messageTestId', - traceId, - timestamp: Date.now(), - correlationId: 'messageCorrelationId', - principalId: 'messagePrincipalId', - tenantId: 'messageTenantId', - sender, - receiver, - contentType: 'application/json', - contentEncoding: 'utf-8', - payload: { - status: statusCode, - message, - data, - }, - } - - const error = UnhandledError.fromMessage(commandErrorResponse) - - const response = error.getErrorResponse() - - expect(response.data).toEqual(data) - expect(response.message).toEqual(message) - expect(response.status).toEqual(statusCode) - expect(response.traceId).toEqual(traceId) - - const strResponse = error.toString() - const result = JSON.parse(strResponse) - - expect(result.data).toEqual(data) - expect(result.message).toEqual(message) - expect(result.status).toEqual(statusCode) - expect(result.traceId).toEqual(traceId) - }) - - it('returns a HandledError from UnhandledError', () => { - const statusCode = StatusCode.BadRequest - const message = 'invalid input' - const data = { some: 'data' } - const traceId = 'messageTraceId' - - const unhandledError = new UnhandledError(statusCode, message, data, traceId) - - const error = unhandledError.intoHandledError() - - expect(error).toBeInstanceOf(HandledError) - expect(error.data).toEqual(data) - expect(error.message).toEqual(message) - expect(error.errorCode).toEqual(statusCode) - expect(error.traceId).toEqual(traceId) - }) + const sender = { + serviceName: 'SenderService', + serviceVersion: '1.1.1', + serviceTarget: 'senderServiceTarget', + instanceId: 'a', + } + + const receiver = { + serviceName: 'ReceiverService', + serviceVersion: '2.2.2', + serviceTarget: 'receiverServiceTarget', + instanceId: 'a', + } + + it('creates a new UnhandledError', () => { + const statusCode = StatusCode.BadRequest + const message = 'invalid input' + const data = { some: 'data' } + const traceId = 'messageTraceId' + + const error = new UnhandledError(statusCode, message, data, traceId) + + const response = error.getErrorResponse() + + expect(response.data).toEqual(data) + expect(response.message).toEqual(message) + expect(response.status).toEqual(statusCode) + expect(response.traceId).toEqual(traceId) + + const strResponse = error.toString() + const result = JSON.parse(strResponse) + + expect(result.data).toEqual(data) + expect(result.message).toEqual(message) + expect(result.status).toEqual(statusCode) + expect(result.traceId).toEqual(traceId) + }) + + it('creates a UnhandledError from error', () => { + const statusCode = StatusCode.BadRequest + const message = 'invalid input' + const data = { some: 'data' } + const traceId = 'messageTraceId' + + const commandErrorResponse: CommandErrorResponse = { + messageType: EBMessageType.CommandErrorResponse, + isHandledError: false, + id: 'messageTestId', + traceId, + timestamp: Date.now(), + correlationId: 'messageCorrelationId', + principalId: 'messagePrincipalId', + tenantId: 'messageTenantId', + sender, + receiver, + contentType: 'application/json', + contentEncoding: 'utf-8', + payload: { + status: statusCode, + message, + data, + }, + } + + const error = UnhandledError.fromMessage(commandErrorResponse) + + const response = error.getErrorResponse() + + expect(response.data).toEqual(data) + expect(response.message).toEqual(message) + expect(response.status).toEqual(statusCode) + expect(response.traceId).toEqual(traceId) + + const strResponse = error.toString() + const result = JSON.parse(strResponse) + + expect(result.data).toEqual(data) + expect(result.message).toEqual(message) + expect(result.status).toEqual(statusCode) + expect(result.traceId).toEqual(traceId) + }) + + it('returns a HandledError from UnhandledError', () => { + const statusCode = StatusCode.BadRequest + const message = 'invalid input' + const data = { some: 'data' } + const traceId = 'messageTraceId' + + const unhandledError = new UnhandledError(statusCode, message, data, traceId) + + const error = unhandledError.intoHandledError() + + expect(error).toBeInstanceOf(HandledError) + expect(error.data).toEqual(data) + expect(error.message).toEqual(message) + expect(error.errorCode).toEqual(statusCode) + expect(error.traceId).toEqual(traceId) + }) }) diff --git a/packages/core/src/core/EventBridge/EventBridgeBaseClass.impl.ts b/packages/core/src/core/EventBridge/EventBridgeBaseClass.impl.ts index 32b4bc373..9907e0751 100644 --- a/packages/core/src/core/EventBridge/EventBridgeBaseClass.impl.ts +++ b/packages/core/src/core/EventBridge/EventBridgeBaseClass.impl.ts @@ -2,7 +2,7 @@ import type { Context, Span, SpanOptions } from '@opentelemetry/api' import { SpanStatusCode } from '@opentelemetry/api' import { Resource } from '@opentelemetry/resources' import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node' -import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions' +import { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from '@opentelemetry/semantic-conventions' import { initLogger } from '../../DefaultLogger/index.js' import { puristaVersion } from '../../version.js' @@ -17,141 +17,141 @@ import type { EventBridgeConfig, EventBridgeEvents } from './types/index.js' * @group Event bridge */ export class EventBridgeBaseClass extends GenericEventEmitter { - logger: Logger - traceProvider: NodeTracerProvider - - config: Complete> - - name: string - - instanceId: Readonly - - defaultCommandTimeout: Readonly - constructor(name: string, config: EventBridgeConfig) { - super() - this.name = name - const logger = config?.logger ?? initLogger(config?.logLevel) - this.logger = logger.getChildLogger({ name }) - - this.instanceId = config.instanceId ?? getNewInstanceId() - this.config = { - logger: logger.getChildLogger({ name }), - instanceId: this.instanceId, - defaultCommandTimeout: config.defaultCommandTimeout ?? 30000, - spanProcessor: undefined, - ...config, - } - - this.defaultCommandTimeout = config.defaultCommandTimeout ?? 30000 - - const resource = Resource.default().merge( - new Resource({ - [SemanticResourceAttributes.SERVICE_NAME]: name, - [SemanticResourceAttributes.SERVICE_VERSION]: puristaVersion, - }), - ) - - this.traceProvider = new NodeTracerProvider({ - resource, - }) - - if (config?.spanProcessor) { - this.traceProvider.addSpanProcessor(config?.spanProcessor) - } - - this.traceProvider.register() - } - - /** - * Returns open telemetry tracer of this service - * - * @returns Tracer - */ - getTracer() { - return this.traceProvider.getTracer('DefaultEventBridge', puristaVersion) - } - - /** - * Start a child span for opentelemetry tracking - * @param name name of span - * @param opts span options - * @param context optional context - * @param fn function to be executed within the span - * @returns return value of fn - */ - async startActiveSpan( - name: string, - opts: SpanOptions, - context: Context | undefined = undefined, - fn: (span: Span) => Promise, - ): Promise { - const tracer = this.getTracer() - - const callback = async (span: Span) => { - span.setAttribute(PuristaSpanTag.PuristaVersion, puristaVersion) - try { - return await fn(span) - } catch (error) { - let message = 'error' - if (error instanceof Error) { - message = error.message - } - - span.recordException(error as Error) - span.setStatus({ - code: SpanStatusCode.ERROR, - message, - }) - - throw error - } finally { - span.end() - } - } - - return context - ? tracer.startActiveSpan(name, opts, context, callback) - : tracer.startActiveSpan(name, opts, callback) - } - - /** - * Start span for opentelemetry tracking on same level. - * The created span will not become the "active" span within opentelemetry! - * - * This means during logging and similar the spanId of parent span is logged. - * - * Use wrapInSpan for marking points in flow of one bigger function, - * but not to trace the program flow itself - * - * @param name name of span - * @param opts span options - * @param fn function te be executed in the span - * @param context span context - * @returns return value of fn - */ - async wrapInSpan(name: string, opts: SpanOptions, fn: (span: Span) => Promise, context?: Context): Promise { - const tracer = this.getTracer() - const span = tracer.startSpan(name, opts, context) - span.setAttribute(PuristaSpanTag.PuristaVersion, puristaVersion) - try { - return await fn(span) - } catch (error) { - let message = 'error' - if (error instanceof Error) { - message = error.message - } - span.recordException(error as Error) - span.setStatus({ - code: SpanStatusCode.ERROR, - message, - }) - - throw error - } finally { - span.end() - } - } - - async destroy() {} - async start() {} + logger: Logger + traceProvider: NodeTracerProvider + + config: Complete> + + name: string + + instanceId: Readonly + + defaultCommandTimeout: Readonly + constructor(name: string, config: EventBridgeConfig) { + super() + this.name = name + const logger = config?.logger ?? initLogger(config?.logLevel) + this.logger = logger.getChildLogger({ name }) + + this.instanceId = config.instanceId ?? getNewInstanceId() + this.config = { + logger: logger.getChildLogger({ name }), + instanceId: this.instanceId, + defaultCommandTimeout: config.defaultCommandTimeout ?? 30000, + spanProcessor: undefined, + ...config, + } + + this.defaultCommandTimeout = config.defaultCommandTimeout ?? 30000 + + const resource = Resource.default().merge( + new Resource({ + [ATTR_SERVICE_NAME]: name, + [ATTR_SERVICE_VERSION]: puristaVersion, + }), + ) + + this.traceProvider = new NodeTracerProvider({ + resource, + }) + + if (config?.spanProcessor) { + this.traceProvider.addSpanProcessor(config?.spanProcessor) + } + + this.traceProvider.register() + } + + /** + * Returns open telemetry tracer of this service + * + * @returns Tracer + */ + getTracer() { + return this.traceProvider.getTracer('DefaultEventBridge', puristaVersion) + } + + /** + * Start a child span for opentelemetry tracking + * @param name name of span + * @param opts span options + * @param context optional context + * @param fn function to be executed within the span + * @returns return value of fn + */ + async startActiveSpan( + name: string, + opts: SpanOptions, + context: Context | undefined, + fn: (span: Span) => Promise, + ): Promise { + const tracer = this.getTracer() + + const callback = async (span: Span) => { + span.setAttribute(PuristaSpanTag.PuristaVersion, puristaVersion) + try { + return await fn(span) + } catch (error) { + let message = 'error' + if (error instanceof Error) { + message = error.message + } + + span.recordException(error as Error) + span.setStatus({ + code: SpanStatusCode.ERROR, + message, + }) + + throw error + } finally { + span.end() + } + } + + return context + ? tracer.startActiveSpan(name, opts, context, callback) + : tracer.startActiveSpan(name, opts, callback) + } + + /** + * Start span for opentelemetry tracking on same level. + * The created span will not become the "active" span within opentelemetry! + * + * This means during logging and similar the spanId of parent span is logged. + * + * Use wrapInSpan for marking points in flow of one bigger function, + * but not to trace the program flow itself + * + * @param name name of span + * @param opts span options + * @param fn function te be executed in the span + * @param context span context + * @returns return value of fn + */ + async wrapInSpan(name: string, opts: SpanOptions, fn: (span: Span) => Promise, context?: Context): Promise { + const tracer = this.getTracer() + const span = tracer.startSpan(name, opts, context) + span.setAttribute(PuristaSpanTag.PuristaVersion, puristaVersion) + try { + return await fn(span) + } catch (error) { + let message = 'error' + if (error instanceof Error) { + message = error.message + } + span.recordException(error as Error) + span.setStatus({ + code: SpanStatusCode.ERROR, + message, + }) + + throw error + } finally { + span.end() + } + } + + async destroy() {} + async start() {} } diff --git a/packages/core/src/core/EventBridge/types/EventBridge.ts b/packages/core/src/core/EventBridge/types/EventBridge.ts index d90d8830e..8000de842 100644 --- a/packages/core/src/core/EventBridge/types/EventBridge.ts +++ b/packages/core/src/core/EventBridge/types/EventBridge.ts @@ -1,13 +1,13 @@ import type { - Command, - CommandDefinitionMetadataBase, - CommandErrorResponse, - CommandSuccessResponse, - CustomMessage, - DefinitionEventBridgeConfig, - EBMessage, - EBMessageAddress, - Subscription, + Command, + CommandDefinitionMetadataBase, + CommandErrorResponse, + CommandSuccessResponse, + CustomMessage, + DefinitionEventBridgeConfig, + EBMessage, + EBMessageAddress, + Subscription, } from '../../index.js' /** @@ -17,84 +17,84 @@ import type { * @group Event bridge */ export interface EventBridge { - readonly name: string + readonly name: string - readonly instanceId: string - /** - * The default time until when a command invocation automatically returns a time out error - */ - readonly defaultCommandTimeout: number + readonly instanceId: string + /** + * The default time until when a command invocation automatically returns a time out error + */ + readonly defaultCommandTimeout: number - /** - * Start the eventbridge and connect to the underlaying message broker - */ - start(): Promise + /** + * Start the eventbridge and connect to the underlaying message broker + */ + start(): Promise - /** - * Emit a message to the eventbridge without awaiting a result - * @param message the message - */ - emitMessage(message: Omit): Promise> + /** + * Emit a message to the eventbridge without awaiting a result + * @param message the message + */ + emitMessage(message: Omit): Promise> - /** - * Call a command of a service and return the result of this command - * @param input a partial command message - * @param contentType the content type of the message payload - * @param contentEncoding the content encoding of the message - * @param ttl the time to live (timeout) of the invocation - */ - invoke(input: Omit, ttl?: number): Promise + /** + * Call a command of a service and return the result of this command + * @param input a partial command message + * @param contentType the content type of the message payload + * @param contentEncoding the content encoding of the message + * @param ttl the time to live (timeout) of the invocation + */ + invoke(input: Omit, ttl?: number): Promise - /** - * - * @param address the address of the service command (service name, version and command name) - * @param cb the function to be called if a matching command arrives - */ - registerCommand( - address: EBMessageAddress, - cb: ( - message: Command, - ) => Promise< - Readonly> | Readonly> - >, - metadata: CommandDefinitionMetadataBase, - eventBridgeConfig: DefinitionEventBridgeConfig, - ): Promise + /** + * + * @param address the address of the service command (service name, version and command name) + * @param cb the function to be called if a matching command arrives + */ + registerCommand( + address: EBMessageAddress, + cb: ( + message: Command, + ) => Promise< + Readonly> | Readonly> + >, + metadata: CommandDefinitionMetadataBase, + eventBridgeConfig: DefinitionEventBridgeConfig, + ): Promise - /** - * Unregister a service command - * @param address The address (service name, version and command name) of the command to be de-registered - */ - unregisterCommand(address: EBMessageAddress): Promise + /** + * Unregister a service command + * @param address The address (service name, version and command name) of the command to be de-registered + */ + unregisterCommand(address: EBMessageAddress): Promise - /** - * Register a new subscription - * @param subscription the subscription definition - * @param cb the function to be called if a matching message arrives - */ - registerSubscription( - subscription: Subscription, - cb: (message: EBMessage) => Promise | undefined>, - ): Promise + /** + * Register a new subscription + * @param subscription the subscription definition + * @param cb the function to be called if a matching message arrives + */ + registerSubscription( + subscription: Subscription, + cb: (message: EBMessage) => Promise | undefined>, + ): Promise - /** - * - * @param address - */ - unregisterSubscription(address: EBMessageAddress): Promise + /** + * + * @param address + */ + unregisterSubscription(address: EBMessageAddress): Promise - /** - * Indicates if the eventbridge has been started and is connected to underlaying message broker - */ - isReady(): Promise + /** + * Indicates if the eventbridge has been started and is connected to underlaying message broker + */ + isReady(): Promise - /** - * Indicates if the eventbridge is running and works correctly - */ - isHealthy(): Promise + /** + * Indicates if the eventbridge is running and works correctly + */ + isHealthy(): Promise - /** - * Shut down event bridge as gracefully as possible - */ - destroy(): Promise + /** + * Shut down event bridge as gracefully as possible + */ + destroy(): Promise } diff --git a/packages/core/src/core/EventBridge/types/EventBridgeConfig.ts b/packages/core/src/core/EventBridge/types/EventBridgeConfig.ts index 3ce71c260..2678486e5 100644 --- a/packages/core/src/core/EventBridge/types/EventBridgeConfig.ts +++ b/packages/core/src/core/EventBridge/types/EventBridgeConfig.ts @@ -1,6 +1,6 @@ import type { SpanProcessor } from '@opentelemetry/sdk-trace-node' -import type { Logger, LogLevelName, Prettify } from '../../types/index.js' +import type { LogLevelName, Logger, Prettify } from '../../types/index.js' /** * The config object for an event bridge. @@ -8,21 +8,21 @@ import type { Logger, LogLevelName, Prettify } from '../../types/index.js' * */ export type EventBridgeConfig = Prettify< - { - /** A logger instance */ - logger?: Logger - /** - * If no logger instance is given, use this log level - */ - logLevel?: LogLevelName - /** A OpenTelemetry span processor */ - spanProcessor?: SpanProcessor | undefined - /** The instance id of the event bridge. - * If not set, a id will generated each time a instance is created. - * Use this if there is a need to always have the same instance id. - * */ - instanceId?: string - /** Overwrite the hardcoded default timeout of command invocations */ - defaultCommandTimeout?: number - } & CustomConfig + { + /** A logger instance */ + logger?: Logger + /** + * If no logger instance is given, use this log level + */ + logLevel?: LogLevelName + /** A OpenTelemetry span processor */ + spanProcessor?: SpanProcessor | undefined + /** The instance id of the event bridge. + * If not set, a id will generated each time a instance is created. + * Use this if there is a need to always have the same instance id. + * */ + instanceId?: string + /** Overwrite the hardcoded default timeout of command invocations */ + defaultCommandTimeout?: number + } & CustomConfig > diff --git a/packages/core/src/core/EventBridge/types/EventBridgeEvents.ts b/packages/core/src/core/EventBridge/types/EventBridgeEvents.ts index 1c3b8467b..7e6aae563 100644 --- a/packages/core/src/core/EventBridge/types/EventBridgeEvents.ts +++ b/packages/core/src/core/EventBridge/types/EventBridgeEvents.ts @@ -1,15 +1,15 @@ import type { UnhandledError } from '../../Error/UnhandledError.impl.js' -import type { addPrefixToObject, Prettify } from '../../types/index.js' +import type { Prettify, addPrefixToObject } from '../../types/index.js' export enum EventBridgeEventNames { - EventbridgeConnected = 'eventbridge-connected', - EventbridgeConnectionError = 'eventbridge-connection-error', + EventbridgeConnected = 'eventbridge-connected', + EventbridgeConnectionError = 'eventbridge-connection-error', - EventbridgeDisconnected = 'eventbridge-disconnected', + EventbridgeDisconnected = 'eventbridge-disconnected', - EventbridgeReconnecting = 'eventbridge-reconnecting', + EventbridgeReconnecting = 'eventbridge-reconnecting', - EventbridgeError = 'eventbridge-error', + EventbridgeError = 'eventbridge-error', } /** @@ -18,34 +18,34 @@ export enum EventBridgeEventNames { * @group Event bridge */ export type EventBridgeEventsBasic = { - /** emitted when then connection to event bridge is established @event */ - [EventBridgeEventNames.EventbridgeConnected]: never + /** emitted when then connection to event bridge is established @event */ + [EventBridgeEventNames.EventbridgeConnected]: never - /** emitted when the connection to event bridge can not be established or a connection has issues or gets closed unexpectedly @event */ - [EventBridgeEventNames.EventbridgeConnectionError]: undefined | unknown | Error + /** emitted when the connection to event bridge can not be established or a connection has issues or gets closed unexpectedly @event */ + [EventBridgeEventNames.EventbridgeConnectionError]: undefined | unknown | Error - /** emitted when the connection to event bridge closed @event */ - [EventBridgeEventNames.EventbridgeDisconnected]: never + /** emitted when the connection to event bridge closed @event */ + [EventBridgeEventNames.EventbridgeDisconnected]: never - /** emitted on retry to connect to event bridge @event */ - [EventBridgeEventNames.EventbridgeReconnecting]: never + /** emitted on retry to connect to event bridge @event */ + [EventBridgeEventNames.EventbridgeReconnecting]: never - /** emitted on internal event bridge error @event */ - [EventBridgeEventNames.EventbridgeError]: UnhandledError | unknown + /** emitted on internal event bridge error @event */ + [EventBridgeEventNames.EventbridgeError]: UnhandledError | unknown } export type EventBridgeCustomEvents = { - /** emitted a EBMessage if event name is provided and if it is enabled and supported on the event bridge @event */ - [key: string]: unknown + /** emitted a EBMessage if event name is provided and if it is enabled and supported on the event bridge @event */ + [key: string]: unknown } export type EventBridgeAdapterEvents = { - /** currently not used, but reserved for further events @event */ - [key: string]: unknown + /** currently not used, but reserved for further events @event */ + [key: string]: unknown } export type EventBridgeEvents = Prettify< - EventBridgeEventsBasic & - addPrefixToObject & - addPrefixToObject + EventBridgeEventsBasic & + addPrefixToObject & + addPrefixToObject > diff --git a/packages/core/src/core/HttpServer/types/HttpExposedServiceMeta.ts b/packages/core/src/core/HttpServer/types/HttpExposedServiceMeta.ts index e6accab5e..84802fd25 100644 --- a/packages/core/src/core/HttpServer/types/HttpExposedServiceMeta.ts +++ b/packages/core/src/core/HttpServer/types/HttpExposedServiceMeta.ts @@ -1,22 +1,22 @@ -import type { CommandDefinitionMetadataBase, Prettify, StatusCode } from '../../index.js' +import type { CommandDefinitionMetadataBase, EmptyObject, Prettify, StatusCode } from '../../index.js' import type { QueryParameter } from './QueryParameter.js' -export type HttpExposedServiceMeta = Prettify< - CommandDefinitionMetadataBase & { - expose: { - http: { - method: 'GET' | 'POST' | 'PATCH' | 'PUT' | 'DELETE' - path: string - openApi?: { - isSecure: boolean - description: string - summary: string - tags?: string[] - query?: QueryParameter[] - additionalStatusCodes?: StatusCode[] - operationId?: string - } - } - } - } +export type HttpExposedServiceMeta = Prettify< + CommandDefinitionMetadataBase & { + expose: { + http: { + method: 'GET' | 'POST' | 'PATCH' | 'PUT' | 'DELETE' + path: string + openApi?: { + isSecure: boolean + description: string + summary: string + tags?: string[] + query?: QueryParameter[] + additionalStatusCodes?: StatusCode[] + operationId?: string + } + } + } + } > diff --git a/packages/core/src/core/HttpServer/types/QueryParameter.ts b/packages/core/src/core/HttpServer/types/QueryParameter.ts index 37b465b42..190870327 100644 --- a/packages/core/src/core/HttpServer/types/QueryParameter.ts +++ b/packages/core/src/core/HttpServer/types/QueryParameter.ts @@ -1,4 +1,6 @@ -export type QueryParameter = { - required: boolean - name: keyof ParameterType +import type { EmptyObject } from '../../types/index.js' + +export type QueryParameter = { + required: boolean + name: keyof ParameterType } diff --git a/packages/core/src/core/HttpServer/types/isHttpExposedServiceMeta.impl.ts b/packages/core/src/core/HttpServer/types/isHttpExposedServiceMeta.impl.ts index a347ffabc..5a4e6b752 100644 --- a/packages/core/src/core/HttpServer/types/isHttpExposedServiceMeta.impl.ts +++ b/packages/core/src/core/HttpServer/types/isHttpExposedServiceMeta.impl.ts @@ -6,11 +6,11 @@ import type { HttpExposedServiceMeta } from './HttpExposedServiceMeta.js' * @returns boolean - true if input is type of HttpExposedServiceMeta */ export const isHttpExposedServiceMeta = (input?: any): input is HttpExposedServiceMeta => { - if (!input || typeof input !== 'object') { - return false - } - if (!input.expose?.http) { - return false - } - return true + if (!input || typeof input !== 'object') { + return false + } + if (!input.expose?.http) { + return false + } + return true } diff --git a/packages/core/src/core/HttpServer/types/isHttpExposedServiceMeta.test.ts b/packages/core/src/core/HttpServer/types/isHttpExposedServiceMeta.test.ts index 48bf7d1c6..aeafebffe 100644 --- a/packages/core/src/core/HttpServer/types/isHttpExposedServiceMeta.test.ts +++ b/packages/core/src/core/HttpServer/types/isHttpExposedServiceMeta.test.ts @@ -2,47 +2,47 @@ import type { HttpExposedServiceMeta } from './HttpExposedServiceMeta.js' import { isHttpExposedServiceMeta } from './isHttpExposedServiceMeta.impl.js' describe('isHttpExposedServiceMeta', () => { - it('returns true if given input is type of HttpExposedServiceMeta', () => { - const meta: HttpExposedServiceMeta = { - expose: { - http: { - method: 'GET', - path: '/test', - }, - }, - } - - const result = isHttpExposedServiceMeta(meta) - expect(result).toBeTruthy() - }) - - it('returns false if given input is type of string', () => { - const result = isHttpExposedServiceMeta('some string') - expect(result).toBeFalsy() - }) - - it('returns false if given input is type of object', () => { - const result = isHttpExposedServiceMeta({ some: { object: 'thing' } }) - expect(result).toBeFalsy() - }) - - it('returns false if given input is type of array', () => { - const result = isHttpExposedServiceMeta(['a', 'b']) - expect(result).toBeFalsy() - }) - - it('returns false if given input is type of number', () => { - const result = isHttpExposedServiceMeta(1) - expect(result).toBeFalsy() - }) - - it('returns false if given input is type of null', () => { - const result = isHttpExposedServiceMeta(null) - expect(result).toBeFalsy() - }) - - it('returns false if given input is type of undefined', () => { - const result = isHttpExposedServiceMeta(undefined) - expect(result).toBeFalsy() - }) + it('returns true if given input is type of HttpExposedServiceMeta', () => { + const meta: HttpExposedServiceMeta = { + expose: { + http: { + method: 'GET', + path: '/test', + }, + }, + } + + const result = isHttpExposedServiceMeta(meta) + expect(result).toBeTruthy() + }) + + it('returns false if given input is type of string', () => { + const result = isHttpExposedServiceMeta('some string') + expect(result).toBeFalsy() + }) + + it('returns false if given input is type of object', () => { + const result = isHttpExposedServiceMeta({ some: { object: 'thing' } }) + expect(result).toBeFalsy() + }) + + it('returns false if given input is type of array', () => { + const result = isHttpExposedServiceMeta(['a', 'b']) + expect(result).toBeFalsy() + }) + + it('returns false if given input is type of number', () => { + const result = isHttpExposedServiceMeta(1) + expect(result).toBeFalsy() + }) + + it('returns false if given input is type of null', () => { + const result = isHttpExposedServiceMeta(null) + expect(result).toBeFalsy() + }) + + it('returns false if given input is type of undefined', () => { + const result = isHttpExposedServiceMeta(undefined) + expect(result).toBeFalsy() + }) }) diff --git a/packages/core/src/core/SecretStore/SecretStoreBaseClass.impl.ts b/packages/core/src/core/SecretStore/SecretStoreBaseClass.impl.ts index 4ff93dcee..405e4d398 100644 --- a/packages/core/src/core/SecretStore/SecretStoreBaseClass.impl.ts +++ b/packages/core/src/core/SecretStore/SecretStoreBaseClass.impl.ts @@ -1,7 +1,7 @@ import { initLogger } from '../../DefaultLogger/index.js' import type { ObjectWithKeysFromStringArray } from '../../helper/index.js' import { UnhandledError } from '../Error/index.js' -import type { Logger, Prettify, StoreBaseConfig } from '../types/index.js' +import type { EmptyObject, Logger, Prettify, StoreBaseConfig } from '../types/index.js' import { StatusCode } from '../types/index.js' import type { SecretStoreCacheMap } from './types/index.js' @@ -17,123 +17,123 @@ import type { SecretStoreCacheMap } from './types/index.js' * * @group Store */ -export abstract class SecretStoreBaseClass = {}> { - logger: Logger - config: Prettify> - - name: string - - cache: SecretStoreCacheMap = new Map() - - constructor(name: string, config: StoreBaseConfig) { - const logger = config?.logger ?? initLogger(config?.logLevel) - this.logger = logger.getChildLogger({ name }) - - this.name = name - - this.config = { - enableGet: true, - enableSet: false, - enableRemove: false, - enableCache: false, - ...config, - } - } - - protected abstract getSecretImpl( - // eslint-disable-next-line @typescript-eslint/no-unused-vars - ...secretNames: SecretNames - ): Promise> - - async getSecret( - ...secretNames: SecretNames - ): Promise> { - if (!this.config.enableGet) { - const err = new UnhandledError(StatusCode.Unauthorized, 'get secret from store is disabled by config') - this.logger.error({ err }, err.message) - throw err - } - - if (!this.config.enableGet) { - throw new UnhandledError(StatusCode.Unauthorized, 'get secret from store is disabled by config') - } - - if (!this.config.enableCache) { - return this.getSecretImpl(...secretNames) - } - - const result: Record = {} - const toFetch: string[] = [] - - secretNames.forEach((secret) => { - const cachedValue = this.cache.get(secret) - result[secret] = undefined - if (cachedValue) { - if (this.config.cacheTtl !== undefined) { - if (cachedValue.createdAt + this.config.cacheTtl >= Date.now()) { - result[secret] = cachedValue.value - } else { - toFetch.push(secret) - } - } else { - result[secret] = cachedValue.value - } - } - }) - - if (!toFetch.length) { - return result as ObjectWithKeysFromStringArray - } - - const freshSecrets = await this.getSecretImpl(...toFetch) - - toFetch.forEach((secret) => { - const value = freshSecrets[secret] - if (value !== undefined) { - this.cache.set(secret, { value, createdAt: Date.now() }) - } else { - this.cache.delete(secret) - } - }) - - return { ...result, ...freshSecrets } as ObjectWithKeysFromStringArray - } - - protected abstract removeSecretImpl(_secretName: string): Promise - - async removeSecret(secretName: string): Promise { - if (!this.config.enableRemove) { - const err = new UnhandledError(StatusCode.Unauthorized, 'remove secret from store is disabled by config') - this.logger.error({ err }, err.message) - throw err - } - - if (this.config.enableCache) { - this.cache.delete(secretName) - } - - return this.removeSecretImpl(secretName) - } - - protected abstract setSecretImpl(_secretName: string, _secretValue: string): Promise - - async setSecret(secretName: string, secretValue: string) { - if (!this.config.enableSet) { - const err = new UnhandledError(StatusCode.Unauthorized, 'set secret at store is disabled by config') - this.logger.error({ err }, err.message) - throw err - } - - const result = await this.setSecretImpl(secretName, secretValue) - - if (this.config.enableCache) { - this.cache.set(secretName, { value: secretValue, createdAt: Date.now() }) - } - - return result - } - - async destroy() { - this.logger.info('stopped') - } +export abstract class SecretStoreBaseClass = EmptyObject> { + logger: Logger + config: Prettify> + + name: string + + cache: SecretStoreCacheMap = new Map() + + constructor(name: string, config: StoreBaseConfig) { + const logger = config?.logger ?? initLogger(config?.logLevel) + this.logger = logger.getChildLogger({ name }) + + this.name = name + + this.config = { + enableGet: true, + enableSet: false, + enableRemove: false, + enableCache: false, + ...config, + } + } + + protected abstract getSecretImpl( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + ...secretNames: SecretNames + ): Promise> + + async getSecret( + ...secretNames: SecretNames + ): Promise> { + if (!this.config.enableGet) { + const err = new UnhandledError(StatusCode.Unauthorized, 'get secret from store is disabled by config') + this.logger.error({ err }, err.message) + throw err + } + + if (!this.config.enableGet) { + throw new UnhandledError(StatusCode.Unauthorized, 'get secret from store is disabled by config') + } + + if (!this.config.enableCache) { + return this.getSecretImpl(...secretNames) + } + + const result: Record = {} + const toFetch: string[] = [] + + for (const secret of secretNames) { + const cachedValue = this.cache.get(secret) + result[secret] = undefined + if (cachedValue) { + if (this.config.cacheTtl !== undefined) { + if (cachedValue.createdAt + this.config.cacheTtl >= Date.now()) { + result[secret] = cachedValue.value + } else { + toFetch.push(secret) + } + } else { + result[secret] = cachedValue.value + } + } + } + + if (!toFetch.length) { + return result as ObjectWithKeysFromStringArray + } + + const freshSecrets = await this.getSecretImpl(...toFetch) + + for (const secret of toFetch) { + const value = freshSecrets[secret] + if (value !== undefined) { + this.cache.set(secret, { value, createdAt: Date.now() }) + } else { + this.cache.delete(secret) + } + } + + return { ...result, ...freshSecrets } as ObjectWithKeysFromStringArray + } + + protected abstract removeSecretImpl(_secretName: string): Promise + + async removeSecret(secretName: string): Promise { + if (!this.config.enableRemove) { + const err = new UnhandledError(StatusCode.Unauthorized, 'remove secret from store is disabled by config') + this.logger.error({ err }, err.message) + throw err + } + + if (this.config.enableCache) { + this.cache.delete(secretName) + } + + return this.removeSecretImpl(secretName) + } + + protected abstract setSecretImpl(_secretName: string, _secretValue: string): Promise + + async setSecret(secretName: string, secretValue: string) { + if (!this.config.enableSet) { + const err = new UnhandledError(StatusCode.Unauthorized, 'set secret at store is disabled by config') + this.logger.error({ err }, err.message) + throw err + } + + const result = await this.setSecretImpl(secretName, secretValue) + + if (this.config.enableCache) { + this.cache.set(secretName, { value: secretValue, createdAt: Date.now() }) + } + + return result + } + + async destroy() { + this.logger.info('stopped') + } } diff --git a/packages/core/src/core/SecretStore/secretStoreBaseClass.test.ts b/packages/core/src/core/SecretStore/secretStoreBaseClass.test.ts index a4b0fe892..8219e2513 100644 --- a/packages/core/src/core/SecretStore/secretStoreBaseClass.test.ts +++ b/packages/core/src/core/SecretStore/secretStoreBaseClass.test.ts @@ -8,88 +8,88 @@ import { StatusCode } from '../types/index.js' import { SecretStoreBaseClass } from './SecretStoreBaseClass.impl.js' class TestClass extends SecretStoreBaseClass { - protected getSecretImpl( - ..._secretNames: SecretNames - ): Promise> { - throw new Error('Not implemented') - } - - protected setSecretImpl(_secretName: string, _secretValue: string): Promise { - throw new Error('Not implemented') - } - - protected removeSecretImpl(_secretName: string): Promise { - throw new Error('Not implemented') - } + protected getSecretImpl( + ..._secretNames: SecretNames + ): Promise> { + throw new Error('Not implemented') + } + + protected setSecretImpl(_secretName: string, _secretValue: string): Promise { + throw new Error('Not implemented') + } + + protected removeSecretImpl(_secretName: string): Promise { + throw new Error('Not implemented') + } } describe('SecretStoreBaseClass', () => { - let sandbox: SinonSandbox - let secretStore: SecretStoreBaseClass - let logger: ReturnType + let sandbox: SinonSandbox + let secretStore: SecretStoreBaseClass + let logger: ReturnType - beforeEach(() => { - sandbox = createSandbox() - logger = getLoggerMock(sandbox) - secretStore = new TestClass('test', { logger: logger.mock }) - }) + beforeEach(() => { + sandbox = createSandbox() + logger = getLoggerMock(sandbox) + secretStore = new TestClass('test', { logger: logger.mock }) + }) - afterEach(() => { - sandbox.restore() - }) + afterEach(() => { + sandbox.restore() + }) - describe('getSecret', () => { - it('should throw an UnhandledError if enableGet is false', async () => { - sandbox.stub(secretStore.config, 'enableGet').value(false) + describe('getSecret', () => { + it('should throw an UnhandledError if enableGet is false', async () => { + sandbox.stub(secretStore.config, 'enableGet').value(false) - await expect(secretStore.getSecret('test')).rejects.toEqual( - new UnhandledError(StatusCode.Unauthorized, 'get secret from store is disabled by config'), - ) + await expect(secretStore.getSecret('test')).rejects.toEqual( + new UnhandledError(StatusCode.Unauthorized, 'get secret from store is disabled by config'), + ) - sandbox.assert.calledOnce(logger.stubs.error) - }) + sandbox.assert.calledOnce(logger.stubs.error) + }) - it('should throw an UnhandledError if enableGet is true but method is not implemented', async () => { - sandbox.stub(secretStore.config, 'enableGet').value(true) + it('should throw an UnhandledError if enableGet is true but method is not implemented', async () => { + sandbox.stub(secretStore.config, 'enableGet').value(true) - await expect(secretStore.getSecret('test')).rejects.toEqual(new Error('Not implemented')) - }) - }) + await expect(secretStore.getSecret('test')).rejects.toEqual(new Error('Not implemented')) + }) + }) - describe('setSecret', () => { - it('should throw an UnhandledError if enableSet is false', async () => { - sandbox.stub(secretStore.config, 'enableSet').value(false) + describe('setSecret', () => { + it('should throw an UnhandledError if enableSet is false', async () => { + sandbox.stub(secretStore.config, 'enableSet').value(false) - await expect(secretStore.setSecret('test', 'secret_value')).rejects.toEqual( - new UnhandledError(StatusCode.Unauthorized, 'set secret at store is disabled by config'), - ) + await expect(secretStore.setSecret('test', 'secret_value')).rejects.toEqual( + new UnhandledError(StatusCode.Unauthorized, 'set secret at store is disabled by config'), + ) - sandbox.assert.calledOnce(logger.stubs.error) - }) + sandbox.assert.calledOnce(logger.stubs.error) + }) - it('should throw an UnhandledError if enableSet is true but method is not implemented', async () => { - // Arrange - sandbox.stub(secretStore.config, 'enableSet').value(true) + it('should throw an UnhandledError if enableSet is true but method is not implemented', async () => { + // Arrange + sandbox.stub(secretStore.config, 'enableSet').value(true) - await expect(secretStore.setSecret('test', 'secret_value')).rejects.toEqual(new Error('Not implemented')) - }) - }) + await expect(secretStore.setSecret('test', 'secret_value')).rejects.toEqual(new Error('Not implemented')) + }) + }) - describe('removeSecret', () => { - it('should throw an UnhandledError if enableRemove is false', async () => { - sandbox.stub(secretStore.config, 'enableRemove').value(false) + describe('removeSecret', () => { + it('should throw an UnhandledError if enableRemove is false', async () => { + sandbox.stub(secretStore.config, 'enableRemove').value(false) - await expect(secretStore.removeSecret('test')).rejects.toMatchObject( - new UnhandledError(StatusCode.Unauthorized, 'remove secret from store is disabled by config'), - ) + await expect(secretStore.removeSecret('test')).rejects.toMatchObject( + new UnhandledError(StatusCode.Unauthorized, 'remove secret from store is disabled by config'), + ) - sandbox.assert.calledOnce(logger.stubs.error) - }) + sandbox.assert.calledOnce(logger.stubs.error) + }) - it('should throw an UnhandledError if enableRemove is true but method is not implemented', async () => { - sandbox.stub(secretStore.config, 'enableRemove').value(true) + it('should throw an UnhandledError if enableRemove is true but method is not implemented', async () => { + sandbox.stub(secretStore.config, 'enableRemove').value(true) - await expect(secretStore.removeSecret('test')).rejects.toMatchObject(new Error('Not implemented')) - }) - }) + await expect(secretStore.removeSecret('test')).rejects.toMatchObject(new Error('Not implemented')) + }) + }) }) diff --git a/packages/core/src/core/SecretStore/types/SecretGetterFunction.ts b/packages/core/src/core/SecretStore/types/SecretGetterFunction.ts index 924c2a2d0..b72a0424a 100644 --- a/packages/core/src/core/SecretStore/types/SecretGetterFunction.ts +++ b/packages/core/src/core/SecretStore/types/SecretGetterFunction.ts @@ -2,5 +2,5 @@ import type { ObjectWithKeysFromStringArray } from '../../../helper/types/Object /** get a secret from the secret store @group Store */ export type SecretGetterFunction = ( - ...secretNames: SecretNames + ...secretNames: SecretNames ) => Promise> diff --git a/packages/core/src/core/SecretStore/types/SecretStore.ts b/packages/core/src/core/SecretStore/types/SecretStore.ts index 838836cec..5d6c534be 100644 --- a/packages/core/src/core/SecretStore/types/SecretStore.ts +++ b/packages/core/src/core/SecretStore/types/SecretStore.ts @@ -7,33 +7,33 @@ import type { SecretSetterFunction } from './SecretSetterFunction.js' * @group Store */ export interface SecretStore { - /** name of store */ - name: string - /** - * get a secret - * @param string name of secret - * @returns the secret - * @throws UnhandledError - */ - getSecret: SecretGetterFunction + /** name of store */ + name: string + /** + * get a secret + * @param string name of secret + * @returns the secret + * @throws UnhandledError + */ + getSecret: SecretGetterFunction - /** - * delete a secret - * @param string name of secret - * @throws UnhandledError - */ - removeSecret: SecretDeleteFunction + /** + * delete a secret + * @param string name of secret + * @throws UnhandledError + */ + removeSecret: SecretDeleteFunction - /** - * set a secret - * @param string name of secret - * @param value value of secret - * @throws UnhandledError - */ - setSecret: SecretSetterFunction + /** + * set a secret + * @param string name of secret + * @param value value of secret + * @throws UnhandledError + */ + setSecret: SecretSetterFunction - /** - * disconnects and shuts down the secret store - */ - destroy(): Promise + /** + * disconnects and shuts down the secret store + */ + destroy(): Promise } diff --git a/packages/core/src/core/Service/Service.impl.ts b/packages/core/src/core/Service/Service.impl.ts index d0b5aaf7d..519df00bd 100644 --- a/packages/core/src/core/Service/Service.impl.ts +++ b/packages/core/src/core/Service/Service.impl.ts @@ -9,50 +9,53 @@ import { puristaVersion } from '../../version.js' import type { ConfigDeleteFunction, ConfigGetterFunction, ConfigSetterFunction } from '../ConfigStore/index.js' import { HandledError } from '../Error/HandledError.impl.js' import { UnhandledError } from '../Error/UnhandledError.impl.js' -import { - createErrorResponse, - createInfoMessage, - createInvokeFunctionProxy, - createSuccessResponse, - deserializeOtp, - getCleanedMessage, - serializeOtp, -} from '../helper/index.js' import type { SecretDeleteFunction, SecretGetterFunction, SecretSetterFunction } from '../SecretStore/index.js' import type { StateDeleteFunction, StateGetterFunction, StateSetterFunction } from '../StateStore/index.js' +import { + createErrorResponse, + createInfoMessage, + createInvokeFunctionProxy, + createSuccessResponse, + deserializeOtp, + getCleanedMessage, + serializeOtp, +} from '../helper/index.js' import type { - Command, - CommandDefinition, - CommandDefinitionListResolved, - CommandFunctionContext, - ContextBase, - CustomMessage, - EBMessage, - EBMessageAddress, - EBMessageSenderAddress, - EmitSchemaList, - InfoMessageType, - Logger, - PrincipalId, - ServiceClass, - ServiceConstructorInput, - Subscription, - SubscriptionDefinition, - SubscriptionDefinitionListResolved, - SubscriptionFunctionContext, - TenantId, - TraceId, + Command, + CommandDefinition, + CommandDefinitionListResolved, + CommandFunctionContext, + ContextBase, + CustomMessage, + EBMessage, + EBMessageAddress, + EBMessageSenderAddress, + EmitSchemaList, + EmptyObject, + InfoMessageType, + InvokeList, + Logger, + PrincipalId, + ServiceClass, + ServiceClassTypes, + ServiceConstructorInput, + Subscription, + SubscriptionDefinition, + SubscriptionDefinitionListResolved, + SubscriptionFunctionContext, + TenantId, + TraceId, } from '../types/index.js' import { - EBMessageType, - PuristaSpanName, - PuristaSpanTag, - ServiceEventsNames, - StatusCode, - StoreType, + EBMessageType, + PuristaSpanName, + PuristaSpanTag, + ServiceEventsNames, + StatusCode, + StoreType, } from '../types/index.js' -import { commandTransformInput } from './commandTransformInput.impl.js' import { ServiceBaseClass } from './ServiceBaseClass/index.js' +import { commandTransformInput } from './commandTransformInput.impl.js' import { subscriptionTransformInput } from './subscriptionTransformInput.impl.js' /** @@ -78,985 +81,1029 @@ import { subscriptionTransformInput } from './subscriptionTransformInput.impl.js * * @group Service */ -export class Service extends ServiceBaseClass implements ServiceClass { - protected subscriptions = new Map() - protected commands = new Map() - - public commandDefinitionList: CommandDefinitionListResolved - public subscriptionDefinitionList: SubscriptionDefinitionListResolved - public config: ConfigType - - public isStarted: boolean = false - - constructor(config: ServiceConstructorInput) { - super({ - logger: config.logger, - info: config.info, - eventBridge: config.eventBridge, - spanProcessor: config.spanProcessor, - secretStore: config.secretStore ?? new DefaultSecretStore(), - configStore: config.configStore ?? new DefaultConfigStore(), - stateStore: config.stateStore ?? new DefaultStateStore(), - configSchema: config.configSchema, - }) - - this.config = config.config - this.commandDefinitionList = config.commandDefinitionList - this.subscriptionDefinitionList = config.subscriptionDefinitionList - } - - get name() { - return `${this.info.serviceName}V${this.info.serviceVersion}` - } - - /** - * It connects to the event bridge and subscribes to the topics that are in the subscription list. - */ - async start() { - if (this.isStarted) { - throw new UnhandledError(StatusCode.InternalServerError, 'Service already started') - } - return this.startActiveSpan('purista.start', {}, undefined, async (span) => { - try { - if (this.configSchema) { - const validationResult = await validate(this.configSchema, this.config) - if (!validationResult.success) { - const err = new UnhandledError( - StatusCode.InternalServerError, - `service ${this.serviceInfo.serviceName} ${this.serviceInfo.serviceVersion}: invalid service configuration provided`, - validationResult.issues, - ) - this.logger.error({ ...span.spanContext(), puristaVersion, err }, err.message) - throw err - } - } - - await this.initializeEventbridgeConnect(this.commandDefinitionList, this.subscriptionDefinitionList) - await this.sendServiceInfo(EBMessageType.InfoServiceReady) - this.logger.info( - { ...span.spanContext(), puristaVersion }, - `service ${this.serviceInfo.serviceName} ${this.serviceInfo.serviceVersion} started`, - ) - this.emit(ServiceEventsNames.ServiceStarted) - } catch (err) { - this.logger.error({ err, ...span.spanContext(), puristaVersion }, `failed to start service`) - this.emit(ServiceEventsNames.ServiceUnavailable, err) - throw err - } - - this.isStarted = true - }) - } - - /** - * Connect service to event bridge to receive commands and command responses - */ - protected async initializeEventbridgeConnect( - commandDefinitionList: CommandDefinitionListResolved, - subscriptions: SubscriptionDefinitionListResolved, - ) { - return this.startActiveSpan('purista.initializeEventbridgeConnect', {}, undefined, async (span) => { - const isEventBridgeReady = await this.eventBridge.isHealthy() - - if (!isEventBridgeReady) { - const err = new UnhandledError(StatusCode.ServiceUnavailable, 'eventbridge not healthy') - this.logger.error({ err }, 'Eventbridge is not ready - can not start service') - throw err - } - - // send info message that this service is going to start up now - await this.sendServiceInfo(EBMessageType.InfoServiceInit) - - // register commands for this service - const commandProms = commandDefinitionList.map((command) => this.registerCommand(command)) - await Promise.all(commandProms) - - // register subscriptions for this service - const subscriptionProms = subscriptions.map((subscription) => { - this.logger.debug({ name: subscription.subscriptionName, ...span.spanContext() }, 'start subscription') - return this.registerSubscription(subscription) - }) - await Promise.all(subscriptionProms) - }) - } - - /** - * Broadcast service info message - * @param infoType type of info message - * @param target function name is need in messages like InfoServiceFunctionAdded - */ - protected async sendServiceInfo(infoType: InfoMessageType, target?: string, payload?: Record) { - return this.startActiveSpan('purista.sendServiceInfo', {}, undefined, async (span) => { - const info = createInfoMessage( - infoType, - { - serviceName: this.info.serviceName, - serviceVersion: this.info.serviceVersion, - serviceTarget: target ?? '', - instanceId: this.eventBridge.instanceId, - }, - { payload }, - ) - - const result = await this.eventBridge.emitMessage(info) - - span.end() - return result - }) - } - - protected getInvokeFunction( - serviceTarget: string, - traceId?: TraceId, - principalId?: PrincipalId, - tenantId?: TenantId, - invokes?: Record< - string, - Record> - >, - ) { - const sender: EBMessageSenderAddress = { - serviceName: this.info.serviceName, - serviceVersion: this.info.serviceVersion, - serviceTarget, - instanceId: this.eventBridge.instanceId, - } - - const invokeCommand = async ( - receiver: EBMessageAddress, - invokePayload: unknown, - invokeparameter: unknown, - contentType = 'application/json', - contentEncoding = 'utf-8', - ): Promise => { - let payload = invokePayload - let parameter = invokeparameter - - return await this.startActiveSpan(serviceTarget + '.invoke', {}, undefined, async (span) => { - span.setAttributes({ - [PuristaSpanTag.ReceiverServiceName]: receiver.serviceName, - [PuristaSpanTag.ReceiverServiceVersion]: receiver.serviceVersion, - [PuristaSpanTag.ReceiverServiceTarget]: receiver.serviceTarget, - }) - - const payloadSchema = - invokes?.[receiver.serviceName]?.[receiver.serviceVersion]?.[receiver.serviceTarget]?.payloadSchema - - if (payloadSchema) { - const res = await validate(payloadSchema, payload) - if (!res.success) { - const err = new UnhandledError(StatusCode.BadRequest, 'invoke payload schema validation failed', { - issues: res.issues, - invokedFrom: sender, - responseFrom: receiver, - }) - - span.recordException(err) - span.setStatus({ - code: SpanStatusCode.ERROR, - message: err.message, - }) - - throw err - } - payload = res.data - } - - const parameterSchema = - invokes?.[receiver.serviceName]?.[receiver.serviceVersion]?.[receiver.serviceTarget]?.parameterSchema - - if (parameterSchema) { - const res = await validate(parameterSchema, parameter) - if (!res.success) { - const err = new UnhandledError(StatusCode.BadRequest, 'invoke parameter schema validation failed', { - issues: res.issues, - invokedFrom: sender, - responseFrom: receiver, - }) - - span.recordException(err) - span.setStatus({ - code: SpanStatusCode.ERROR, - message: err.message, - }) - - throw err - } - - parameter = res.data - } - - const msg: Readonly> = Object.freeze({ - messageType: EBMessageType.Command, - traceId, - sender, - receiver, - contentType, - contentEncoding, - payload: { - payload, - parameter, - }, - principalId, - tenantId, - }) - - const outputSchema = - invokes?.[receiver.serviceName]?.[receiver.serviceVersion]?.[receiver.serviceTarget]?.outputSchema - - if (!outputSchema) { - return this.eventBridge.invoke(msg) - } - - const response = await this.eventBridge.invoke(msg) - - const outResult = await validate(outputSchema, response) - - if (outResult.success) { - span.setStatus({ - code: SpanStatusCode.OK, - message: 'OK', - }) - return outResult.data - } - - const err = new UnhandledError( - StatusCode.InternalServerError, - 'invoke response output schema validation failed', - { - issues: outResult.issues, - invokedFrom: sender, - responseFrom: receiver, - }, - ) - - span.recordException(err) - span.setStatus({ - code: SpanStatusCode.ERROR, - message: err.message, - }) - - throw err - }) - } - - return invokeCommand.bind(this) - } - - protected getEmitFunction( - serviceTarget: string, - traceId?: TraceId, - principalId?: PrincipalId, - tenantId?: TenantId, - emitList?: EmitList, - ) { - const sender: EBMessageSenderAddress = { - serviceName: this.info.serviceName, - serviceVersion: this.info.serviceVersion, - serviceTarget, - instanceId: this.eventBridge.instanceId, - } - - const emitCustomEvent = async ( - eventName: K, - eventPayload?: Payload, - contentType = 'application/json', - contentEncoding = 'utf-8', - ) => { - await this.startActiveSpan('purista.emitEvent', {}, undefined, async (span) => { - const eventSchemas = emitList as EmitSchemaList - const schema = eventSchemas[eventName] - - if (!schema) { - const err = new UnhandledError(StatusCode.InternalServerError, `No schema for ${eventName as string} found`, { - eventName, - }) - span.recordException(err) - span.setStatus({ - code: SpanStatusCode.ERROR, - message: err.message, - }) - throw err - } - - const validation = await validate(schema, eventPayload) - if (!validation.success) { - const err = new UnhandledError( - StatusCode.InternalServerError, - `Payload validation for event ${eventName as string} failed`, - { eventName, issues: validation.issues }, - ) - span.recordException(err) - span.setStatus({ - code: SpanStatusCode.ERROR, - message: err.message, - }) - throw err - } - - span.addEvent(eventName as string) - const msg: Readonly>, 'id' | 'timestamp'>> = Object.freeze({ - messageType: EBMessageType.CustomMessage, - traceId, - contentType, - contentEncoding, - sender, - eventName: eventName as string, - payload: validation.data, - principalId, - tenantId, - }) - - const res = this.eventBridge.emitMessage(msg) - - span.setStatus({ - code: SpanStatusCode.OK, - message: 'OK', - }) - - return res - }) - } - - return emitCustomEvent.bind(this) - } - - public getContextFunctions(logger: Logger): ContextBase { - const getSecretFunction = async function (this: Service, ...secretNames: string[]) { - return this.wrapInSpan(PuristaSpanName.SecretStoreGetValue, {}, async (span) => { - try { - span.setAttributes({ - [PuristaSpanTag.StoreName]: this.secretStore.name, - [PuristaSpanTag.StoreType]: StoreType.SecretStore, - }) - return this.secretStore.getSecret(...secretNames) - } catch (err) { - span.recordException(err as Error) - throw err - } - }) - } - const getSecret: SecretGetterFunction = getSecretFunction.bind(this) - - const setSecretFunction = async function (this: Service, secretName: string, value: string) { - return this.wrapInSpan(PuristaSpanName.SecretStoreGetValue, {}, async (span) => { - try { - span.setAttributes({ - [PuristaSpanTag.StoreName]: this.secretStore.name, - [PuristaSpanTag.StoreType]: StoreType.SecretStore, - }) - return this.secretStore.setSecret(secretName, value) - } catch (err) { - span.recordException(err as Error) - throw err - } - }) - } - const setSecret: SecretSetterFunction = setSecretFunction.bind(this) - - const removeSecretFunction = async function (this: Service, secretName: string) { - return this.wrapInSpan(PuristaSpanName.SecretStoreGetValue, {}, async (span) => { - try { - span.setAttributes({ - [PuristaSpanTag.StoreName]: this.secretStore.name, - [PuristaSpanTag.StoreType]: StoreType.SecretStore, - }) - return this.secretStore.removeSecret(secretName) - } catch (err) { - span.recordException(err as Error) - throw err - } - }) - } - const removeSecret: SecretDeleteFunction = removeSecretFunction.bind(this) - - const getConfigFunction = async function (this: Service, ...configNames: string[]) { - return this.wrapInSpan(PuristaSpanName.ConfigStoreGetValue, {}, async (span) => { - try { - span.setAttributes({ - [PuristaSpanTag.StoreName]: this.configStore.name, - [PuristaSpanTag.StoreType]: StoreType.ConfigStore, - }) - return this.configStore.getConfig(...configNames) - } catch (err) { - span.recordException(err as Error) - throw err - } - }) - } - const getConfig: ConfigGetterFunction = getConfigFunction.bind(this) - - const setConfigFunction = async function (this: Service, configName: string, value: unknown) { - return this.wrapInSpan(PuristaSpanName.ConfigStoreGetValue, {}, async (span) => { - try { - span.setAttributes({ - [PuristaSpanTag.StoreName]: this.configStore.name, - [PuristaSpanTag.StoreType]: StoreType.ConfigStore, - }) - return this.configStore.setConfig(configName, value) - } catch (err) { - span.recordException(err as Error) - throw err - } - }) - } - const setConfig: ConfigSetterFunction = setConfigFunction.bind(this) - - const removeConfigFunction = async function (this: Service, configName: string) { - return this.wrapInSpan(PuristaSpanName.ConfigStoreGetValue, {}, async (span) => { - try { - span.setAttributes({ - [PuristaSpanTag.StoreName]: this.configStore.name, - [PuristaSpanTag.StoreType]: StoreType.ConfigStore, - }) - return this.configStore.removeConfig(configName) - } catch (err) { - span.recordException(err as Error) - throw err - } - }) - } - const removeConfig: ConfigDeleteFunction = removeConfigFunction.bind(this) - - const getStateFunction = async function (this: Service, ...stateNames: string[]) { - return this.wrapInSpan(PuristaSpanName.StateStoreGetValue, {}, async (span) => { - try { - span.setAttributes({ - [PuristaSpanTag.StoreName]: this.stateStore.name, - [PuristaSpanTag.StoreType]: StoreType.StateStore, - }) - return this.stateStore.getState(...stateNames) - } catch (err) { - span.recordException(err as Error) - throw err - } - }) - } - const getState: StateGetterFunction = getStateFunction.bind(this) - - const setStateFunction = async function (this: Service, stateName: string, value: unknown) { - return this.wrapInSpan(PuristaSpanName.StateStoreGetValue, {}, async (span) => { - try { - span.setAttributes({ - [PuristaSpanTag.StoreName]: this.stateStore.name, - [PuristaSpanTag.StoreType]: StoreType.StateStore, - }) - return this.stateStore.setState(stateName, value) - } catch (err) { - span.recordException(err as Error) - throw err - } - }) - } - const setState: StateSetterFunction = setStateFunction.bind(this) - - const removeStateFunction = async function (this: Service, stateName: string) { - return this.wrapInSpan(PuristaSpanName.StateStoreGetValue, {}, async (span) => { - try { - span.setAttributes({ - [PuristaSpanTag.StoreName]: this.stateStore.name, - [PuristaSpanTag.StoreType]: StoreType.StateStore, - }) - return this.stateStore.removeState(stateName) - } catch (err) { - span.recordException(err as Error) - throw err - } - }) - } - const removeState: StateDeleteFunction = removeStateFunction.bind(this) - - return { - logger, - wrapInSpan: this.wrapInSpan.bind(this), - startActiveSpan: this.startActiveSpan.bind(this), - secrets: { - getSecret, - setSecret, - removeSecret, - }, - configs: { - getConfig, - setConfig, - removeConfig, - }, - states: { - getState, - setState, - removeState, - }, - } - } - - /** - * Called when a command is received by the service - * - * @param subscriptionId - * @param command - */ - public async executeCommand(message: Readonly) { - const command = this.commands.get(message.receiver.serviceTarget) - - const context = deserializeOtp(this.logger, message.otp) - - return this.startActiveSpan(command?.commandName ?? 'purista.executeCommand', {}, context, async (span) => { - const traceId = message.traceId - - const logger = this.logger.getChildLogger({ - serviceTarget: command?.commandName, - ...span.spanContext(), - customTraceId: traceId, - principalId: message.principalId, - tenantId: message.tenantId, - }) - - if (message.principalId) { - span.setAttribute(PuristaSpanTag.PrincipalId, message.principalId) - } - - if (message.tenantId) { - span.setAttribute(PuristaSpanTag.TenantId, message.tenantId) - } - - if (!command) { - logger.error({ message: getCleanedMessage(message) }, 'received invalid command') - - span.setStatus({ - code: SpanStatusCode.ERROR, - message: 'received invalid command', - }) - return await this.startActiveSpan('sendErrorResponse', {}, undefined, async () => - createErrorResponse(this.eventBridge.instanceId, message, StatusCode.NotImplemented), - ) - } - - try { - const { payload, parameter } = await commandTransformInput(this, logger, command, message) - - let result: unknown = await this.startActiveSpan( - command.commandName + '.functionExecution', - {}, - undefined, - async (_subSpan) => { - const context: CommandFunctionContext = { - message, - emit: this.getEmitFunction( - command.commandName, - traceId, - message.principalId, - message.tenantId, - command.emitList, - ), - invoke: this.getInvokeFunction( - command.commandName, - traceId, - message.principalId, - message.tenantId, - command.invokes, - ), - ...this.getContextFunctions(logger), - service: createInvokeFunctionProxy( - this.getInvokeFunction( - command.commandName, - traceId, - message.principalId, - message.tenantId, - command.invokes, - ), - ), - } - const call = command.call.bind(this, context) - return await call(payload, parameter) - }, - ) - - if (Object.keys(command.hooks.afterGuard ?? {}).length) { - const guards = command.hooks.afterGuard - - await this.startActiveSpan(command.commandName + '.afterGuardHooks', {}, undefined, async () => { - const guardsPromises: Promise[] = [] - - for (const [name, hook] of Object.entries(guards ?? {})) { - const context: CommandFunctionContext = { - message, - emit: this.getEmitFunction( - command.commandName, - traceId, - message.principalId, - message.tenantId, - command.emitList, - ), - invoke: this.getInvokeFunction( - command.commandName, - traceId, - message.principalId, - message.tenantId, - command.invokes, - ), - ...this.getContextFunctions(logger), - service: createInvokeFunctionProxy( - this.getInvokeFunction( - command.commandName, - traceId, - message.principalId, - message.tenantId, - command.invokes, - ), - ), - } - - const guardPromise = this.wrapInSpan('afterGuardHook.' + name, {}, async (_subSpan) => { - return hook.bind(this)(context, result as Readonly, payload, parameter) - }) - guardsPromises.push(guardPromise) - } - - await Promise.all(guardsPromises) - }) - } - - if (command.hooks.transformOutput) { - const transformOutput = command.hooks.transformOutput - await this.startActiveSpan(command.commandName + '.outputTransformation', {}, undefined, async (subSpan) => { - const afterTransform = transformOutput.transformFunction.bind(this, { - message, - ...this.getContextFunctions(logger), - }) - const resultTransformed = await afterTransform(result as Readonly, parameter) - - const validationResult = await validate(transformOutput.transformOutputSchema, resultTransformed) - if (!validationResult.success) { - const err = new UnhandledError(StatusCode.InternalServerError, undefined, validationResult.issues) - subSpan.recordException(err) - logger.warn({ ...subSpan.spanContext(), err }, 'transform output validation failed:', err.message) - - subSpan.setStatus({ - code: SpanStatusCode.ERROR, - message: 'transform output validation failed', - }) - throw err - } - result = validationResult.data - }) - } - - return await this.startActiveSpan(command.commandName + '.success', {}, undefined, async (subSpan) => { - if (command.eventName) { - subSpan.addEvent(command.eventName) - this.emit(`custom-${command.eventName}`, result) - } - return { - ...createSuccessResponse(this.eventBridge.instanceId, message, result, command.eventName), - otp: serializeOtp(), - } - }) - } catch (error) { - span.recordException(error as Error) - - if (error instanceof HandledError) { - this.emit(ServiceEventsNames.CommandHandledError, { commandName: command.commandName, error, traceId }) - span.setStatus({ - code: SpanStatusCode.ERROR, - message: error.message, - }) - - return await this.startActiveSpan('sendErrorResponse', {}, undefined, async () => - createErrorResponse(this.eventBridge.instanceId, message, (error as HandledError).errorCode, error), - ) - } - - this.emit(ServiceEventsNames.CommandUnhandledError, { commandName: command.commandName, error, traceId }) - - logger.error( - { err: error, message: getCleanedMessage(message), ...span.spanContext() }, - 'executeCommand unhandled error', - ) - - span.setStatus({ - code: SpanStatusCode.ERROR, - message: 'executeCommand unhandled error', - }) - - return await this.startActiveSpan(command.commandName + '.error', {}, undefined, async () => - createErrorResponse(this.eventBridge.instanceId, message, StatusCode.InternalServerError, error), - ) - } - }) - } - - public async registerCommand(commandDefinition: CommandDefinition): Promise { - return this.startActiveSpan('purista.registerCommand', {}, undefined, async (span) => { - this.logger.debug({ ...this.serviceInfo, ...span.spanContext() }, 'register command') - - span.setAttributes({ - serviceName: this.serviceInfo.serviceName, - serviceVersion: this.serviceInfo.serviceVersion, - commandName: commandDefinition.commandName, - }) - - this.commands.set(commandDefinition.commandName, commandDefinition) - - await this.eventBridge.registerCommand( - { - serviceName: this.serviceInfo.serviceName, - serviceVersion: this.serviceInfo.serviceVersion, - serviceTarget: commandDefinition.commandName, - }, - this.executeCommand.bind(this), - commandDefinition.metadata, - commandDefinition.eventBridgeConfig, - ) - - span.end() - }) - } - - public async executeSubscription( - message: Readonly, - subscriptionName: string, - ): Promise | undefined> { - const subscription = this.subscriptions.get(subscriptionName) - - const otpContext = deserializeOtp(this.logger, message.otp) - const spanContext = otpContext ? trace.getSpanContext(otpContext) : undefined - - return this.startActiveSpan( - subscriptionName || 'purista.executeSubscription', - { links: spanContext ? [{ context: spanContext }] : [] }, - undefined, - async (span) => { - const traceId = message.traceId - - const logger = this.logger.getChildLogger({ - serviceTarget: subscriptionName, - ...span.spanContext(), - customTraceId: traceId, - principalId: message.principalId, - tenantId: message.tenantId, - }) - - if (message.principalId) { - span.setAttribute(PuristaSpanTag.PrincipalId, message.principalId) - } - - if (message.tenantId) { - span.setAttribute(PuristaSpanTag.TenantId, message.tenantId) - } - - if (!subscription) { - logger.error({ message: getCleanedMessage(message) }, 'received message for invalid subscription') - - span.setStatus({ - code: SpanStatusCode.ERROR, - message: 'received message for invalid subscription', - }) - return - } - - try { - const { payload, parameter } = await subscriptionTransformInput(this, logger, subscription, message) - - let result: unknown = await this.startActiveSpan( - subscription.subscriptionName + '.functionExecution', - {}, - undefined, - async (_subSpan) => { - const context: SubscriptionFunctionContext = { - message, - emit: this.getEmitFunction( - subscriptionName, - traceId, - message.principalId, - message.tenantId, - subscription.emitList, - ), - invoke: this.getInvokeFunction( - subscriptionName, - traceId, - message.principalId, - message.tenantId, - subscription.invokes, - ), - ...this.getContextFunctions(logger), - service: createInvokeFunctionProxy( - this.getInvokeFunction( - subscriptionName, - traceId, - message.principalId, - message.tenantId, - subscription.invokes, - ), - ), - } - const call2 = subscription.call.bind(this, context) - return await call2(payload, parameter) - }, - ) - - if (Object.keys(subscription.hooks.afterGuard ?? {}).length) { - const guards = subscription.hooks.afterGuard - - await this.startActiveSpan(subscription.subscriptionName + '.afterGuardHooks', {}, undefined, async () => { - const guardsPromises: Promise[] = [] - - for (const [name, hook] of Object.entries(guards ?? {})) { - const context: SubscriptionFunctionContext = { - message, - emit: this.getEmitFunction( - subscription.subscriptionName, - traceId, - message.principalId, - message.tenantId, - subscription.emitList, - ), - invoke: this.getInvokeFunction( - subscription.subscriptionName, - traceId, - message.principalId, - message.tenantId, - ), - ...this.getContextFunctions(logger), - service: createInvokeFunctionProxy( - this.getInvokeFunction( - subscription.subscriptionName, - traceId, - message.principalId, - message.tenantId, - ), - ), - } - - const guardPromise = this.wrapInSpan('afterGuardHook.' + name, {}, async (_subSpan) => { - return hook.bind(this)(context, result as Readonly, payload, parameter) - }) - guardsPromises.push(guardPromise) - } - - await Promise.all(guardsPromises) - }) - } - - if (subscription.hooks.transformOutput) { - const transformOutput = subscription.hooks.transformOutput - await this.startActiveSpan( - subscription.subscriptionName + '.outputTransformation', - {}, - undefined, - async (subSpan) => { - const afterTransform = transformOutput.transformFunction.bind(this, { - message, - ...this.getContextFunctions(logger), - }) - const resultTransformed = await afterTransform(result as Readonly, parameter) - - const validationResult = await validate(transformOutput.transformOutputSchema, resultTransformed) - if (!validationResult.success) { - const err = new UnhandledError(StatusCode.InternalServerError, undefined, validationResult.issues) - subSpan.recordException(err) - logger.warn({ ...subSpan.spanContext(), err }, 'transform output validation failed:', err.message) - - subSpan.setStatus({ - code: SpanStatusCode.ERROR, - message: 'transform output validation failed', - }) - throw err - } - result = validationResult.data - }, - ) - } - - if (subscription.emitEventName) { - return await this.startActiveSpan( - subscription.subscriptionName + '.success', - {}, - undefined, - async (subSpan) => { - this.emit(`custom-${subscription.emitEventName}`, result) - subSpan.addEvent(subscription.emitEventName as string) - const resultMsg: Omit = { - messageType: EBMessageType.CustomMessage, - contentType: subscription.metadata.expose.contentTypeResponse ?? 'application/json', - contentEncoding: subscription.metadata.expose.contentEncodingResponse ?? 'utf-8', - sender: { - serviceName: this.serviceInfo.serviceName, - serviceVersion: this.serviceInfo.serviceVersion, - serviceTarget: subscription.subscriptionName, - instanceId: this.eventBridge.instanceId, - }, - payload: result, - eventName: subscription.emitEventName as string, - otp: serializeOtp(), - } - return resultMsg - }, - ) - } - return undefined - } catch (err) { - logger.error({ err }, 'Error in subscription execution') - if (err instanceof HandledError) { - this.emit(ServiceEventsNames.SubscriptionHandledError, { subscriptionName, error: err, traceId }) - // handled errors prevent that the message is re-delivered for retry - return - } - if (err instanceof UnhandledError) { - this.emit(ServiceEventsNames.SubscriptionUnhandledError, { subscriptionName, error: err, traceId }) - } - span.recordException(err as Error) - - span.setStatus({ - code: SpanStatusCode.ERROR, - message: (err as Error).message, - }) - - // re-throw error here, so the underlaying event bridge driver can handle ack/re-delivery for retry - throw err - } - }, - ) - } - - public async registerSubscription(subscriptionDefinition: SubscriptionDefinition): Promise { - return this.startActiveSpan('purista.registerSubscription', {}, undefined, async (span) => { - this.logger.debug({ ...this.serviceInfo, ...span.spanContext() }, 'register subscription') - - span.setAttributes({ - serviceName: this.info.serviceName, - serviceVersion: this.info.serviceVersion, - subscriptionName: subscriptionDefinition.subscriptionName, - }) - - this.subscriptions.set(subscriptionDefinition.subscriptionName, subscriptionDefinition) - - const subscription: Subscription = { - sender: subscriptionDefinition.sender, - receiver: subscriptionDefinition.receiver, - messageType: subscriptionDefinition.messageType, - eventName: subscriptionDefinition.eventName, - emitEventName: subscriptionDefinition.emitEventName, - subscriber: { - serviceName: this.info.serviceName, - serviceVersion: this.info.serviceVersion, - serviceTarget: subscriptionDefinition.subscriptionName, - }, - eventBridgeConfig: subscriptionDefinition.eventBridgeConfig, - principalId: subscriptionDefinition.principalId, - tenantId: subscriptionDefinition.tenantId, - } - - await this.eventBridge.registerSubscription(subscription, (message: EBMessage) => - this.executeSubscription(message, subscriptionDefinition.subscriptionName), - ) - - span.end() - }) - } - - async destroy() { - this.emit(ServiceEventsNames.ServiceDrain) - this.emit(ServiceEventsNames.ServiceStopped) - this.removeAllListeners() - await super.destroy() - } +export class Service + extends ServiceBaseClass + implements ServiceClass +{ + protected subscriptions = new Map< + string, + SubscriptionDefinition + >() + protected commands = new Map< + string, + CommandDefinition + >() + + public commandDefinitionList: CommandDefinitionListResolved + public subscriptionDefinitionList: SubscriptionDefinitionListResolved + public config: S['ConfigType'] + + public resources: S['Resources'] + + public isStarted = false + + constructor(config: ServiceConstructorInput) { + super({ + logger: config.logger, + info: config.info, + eventBridge: config.eventBridge, + spanProcessor: config.spanProcessor, + secretStore: config.secretStore ?? new DefaultSecretStore(), + configStore: config.configStore ?? new DefaultConfigStore(), + stateStore: config.stateStore ?? new DefaultStateStore(), + configSchema: config.configSchema, + }) + + this.config = config.config + this.resources = config.resources ?? {} + this.commandDefinitionList = config.commandDefinitionList + this.subscriptionDefinitionList = config.subscriptionDefinitionList + } + + get name() { + return `${this.info.serviceName}V${this.info.serviceVersion}` + } + + /** + * It connects to the event bridge and subscribes to the topics that are in the subscription list. + */ + async start() { + if (this.isStarted) { + throw new UnhandledError(StatusCode.InternalServerError, 'Service already started') + } + return this.startActiveSpan('purista.start', {}, undefined, async span => { + try { + if (this.configSchema) { + const validationResult = await validate(this.configSchema, this.config) + if (!validationResult.success) { + const err = new UnhandledError( + StatusCode.InternalServerError, + `service ${this.serviceInfo.serviceName} ${this.serviceInfo.serviceVersion}: invalid service configuration provided`, + validationResult.issues, + ) + this.logger.error({ ...span.spanContext(), puristaVersion, err }, err.message) + throw err + } + } + + await this.initializeEventbridgeConnect(this.commandDefinitionList, this.subscriptionDefinitionList) + await this.sendServiceInfo(EBMessageType.InfoServiceReady) + this.logger.info( + { ...span.spanContext(), puristaVersion }, + `service ${this.serviceInfo.serviceName} ${this.serviceInfo.serviceVersion} started`, + ) + this.emit(ServiceEventsNames.ServiceStarted) + } catch (err) { + this.logger.error({ err, ...span.spanContext(), puristaVersion }, 'failed to start service') + this.emit(ServiceEventsNames.ServiceUnavailable, err) + throw err + } + + this.isStarted = true + }) + } + + /** + * Connect service to event bridge to receive commands and command responses + */ + protected async initializeEventbridgeConnect( + commandDefinitionList: CommandDefinitionListResolved, + subscriptions: SubscriptionDefinitionListResolved, + ) { + return this.startActiveSpan('purista.initializeEventbridgeConnect', {}, undefined, async span => { + const isEventBridgeReady = await this.eventBridge.isHealthy() + + if (!isEventBridgeReady) { + const err = new UnhandledError(StatusCode.ServiceUnavailable, 'eventbridge not healthy') + this.logger.error({ err }, 'Eventbridge is not ready - can not start service') + throw err + } + + // send info message that this service is going to start up now + await this.sendServiceInfo(EBMessageType.InfoServiceInit) + + // register commands for this service + const commandProms = commandDefinitionList.map(command => this.registerCommand(command)) + await Promise.all(commandProms) + + // register subscriptions for this service + const subscriptionProms = subscriptions.map(subscription => { + this.logger.debug({ name: subscription.subscriptionName, ...span.spanContext() }, 'start subscription') + return this.registerSubscription(subscription) + }) + await Promise.all(subscriptionProms) + }) + } + + /** + * Broadcast service info message + * @param infoType type of info message + * @param target function name is need in messages like InfoServiceFunctionAdded + */ + protected async sendServiceInfo(infoType: InfoMessageType, target?: string, payload?: Record) { + return this.startActiveSpan('purista.sendServiceInfo', {}, undefined, async span => { + const info = createInfoMessage( + infoType, + { + serviceName: this.info.serviceName, + serviceVersion: this.info.serviceVersion, + serviceTarget: target ?? '', + instanceId: this.eventBridge.instanceId, + }, + { payload }, + ) + + const result = await this.eventBridge.emitMessage(info) + + span.end() + return result + }) + } + + protected getInvokeFunction( + serviceTarget: string, + traceId?: TraceId, + principalId?: PrincipalId, + tenantId?: TenantId, + invokes?: Invokes, + ) { + const sender: EBMessageSenderAddress = { + serviceName: this.info.serviceName, + serviceVersion: this.info.serviceVersion, + serviceTarget, + instanceId: this.eventBridge.instanceId, + } + + const invokeCommand = async ( + receiver: EBMessageAddress, + invokePayload: Payload, + invokeparameter: Parameter, + contentType = 'application/json', + contentEncoding = 'utf-8', + ): Promise => { + let payload = invokePayload + let parameter = invokeparameter + + return await this.startActiveSpan(`${serviceTarget}.invoke`, {}, undefined, async span => { + span.setAttributes({ + [PuristaSpanTag.ReceiverServiceName]: receiver.serviceName, + [PuristaSpanTag.ReceiverServiceVersion]: receiver.serviceVersion, + [PuristaSpanTag.ReceiverServiceTarget]: receiver.serviceTarget, + }) + + const payloadSchema = + invokes?.[receiver.serviceName]?.[receiver.serviceVersion]?.[receiver.serviceTarget]?.payloadSchema + + if (payloadSchema) { + const res = await validate(payloadSchema, payload) + if (!res.success) { + const err = new UnhandledError(StatusCode.BadRequest, 'invoke payload schema validation failed', { + issues: res.issues, + invokedFrom: sender, + responseFrom: receiver, + }) + + span.recordException(err) + span.setStatus({ + code: SpanStatusCode.ERROR, + message: err.message, + }) + + throw err + } + payload = res.data as Payload + } + + const parameterSchema = + invokes?.[receiver.serviceName]?.[receiver.serviceVersion]?.[receiver.serviceTarget]?.parameterSchema + + if (parameterSchema) { + const res = await validate(parameterSchema, parameter) + if (!res.success) { + const err = new UnhandledError(StatusCode.BadRequest, 'invoke parameter schema validation failed', { + issues: res.issues, + invokedFrom: sender, + responseFrom: receiver, + }) + + span.recordException(err) + span.setStatus({ + code: SpanStatusCode.ERROR, + message: err.message, + }) + + throw err + } + + parameter = res.data as Parameter + } + + const msg: Readonly> = Object.freeze({ + messageType: EBMessageType.Command, + traceId, + sender, + receiver, + contentType, + contentEncoding, + payload: { + payload, + parameter, + }, + principalId, + tenantId, + }) + + const outputSchema = + invokes?.[receiver.serviceName]?.[receiver.serviceVersion]?.[receiver.serviceTarget]?.outputSchema + + if (!outputSchema) { + return this.eventBridge.invoke(msg) + } + + const response = await this.eventBridge.invoke(msg) + + const outResult = await validate(outputSchema, response) + + if (outResult.success) { + span.setStatus({ + code: SpanStatusCode.OK, + message: 'OK', + }) + return outResult.data + } + + const err = new UnhandledError( + StatusCode.InternalServerError, + 'invoke response output schema validation failed', + { + issues: outResult.issues, + invokedFrom: sender, + responseFrom: receiver, + }, + ) + + span.recordException(err) + span.setStatus({ + code: SpanStatusCode.ERROR, + message: err.message, + }) + + throw err + }) + } + + return invokeCommand.bind(this) + } + + protected getEmitFunction = EmptyObject>( + serviceTarget: string, + traceId?: TraceId, + principalId?: PrincipalId, + tenantId?: TenantId, + emitList?: EmitList, + ) { + const sender: EBMessageSenderAddress = { + serviceName: this.info.serviceName, + serviceVersion: this.info.serviceVersion, + serviceTarget, + instanceId: this.eventBridge.instanceId, + } + + const emitCustomEvent = async ( + eventName: K, + eventPayload?: Payload, + contentType = 'application/json', + contentEncoding = 'utf-8', + ) => { + await this.startActiveSpan('purista.emitEvent', {}, undefined, async span => { + const eventSchemas = emitList as EmitSchemaList + const schema = eventSchemas[eventName] + + if (!schema) { + const err = new UnhandledError(StatusCode.InternalServerError, `No schema for ${eventName as string} found`, { + eventName, + }) + span.recordException(err) + span.setStatus({ + code: SpanStatusCode.ERROR, + message: err.message, + }) + throw err + } + + const validation = await validate(schema, eventPayload) + if (!validation.success) { + const err = new UnhandledError( + StatusCode.InternalServerError, + `Payload validation for event ${eventName as string} failed`, + { eventName, issues: validation.issues }, + ) + span.recordException(err) + span.setStatus({ + code: SpanStatusCode.ERROR, + message: err.message, + }) + throw err + } + + span.addEvent(eventName as string) + const msg: Readonly>, 'id' | 'timestamp'>> = Object.freeze({ + messageType: EBMessageType.CustomMessage, + traceId, + contentType, + contentEncoding, + sender, + eventName: eventName as string, + payload: validation.data, + principalId, + tenantId, + }) + + const res = this.eventBridge.emitMessage(msg) + + span.setStatus({ + code: SpanStatusCode.OK, + message: 'OK', + }) + + return res + }) + } + + return emitCustomEvent.bind(this) + } + + public getContextFunctions(logger: Logger): ContextBase { + const getSecretFunction = async function (this: Service, ...secretNames: string[]) { + return this.wrapInSpan(PuristaSpanName.SecretStoreGetValue, {}, async span => { + try { + span.setAttributes({ + [PuristaSpanTag.StoreName]: this.secretStore.name, + [PuristaSpanTag.StoreType]: StoreType.SecretStore, + }) + return this.secretStore.getSecret(...secretNames) + } catch (err) { + span.recordException(err as Error) + throw err + } + }) + } + const getSecret: SecretGetterFunction = getSecretFunction.bind(this) + + const setSecretFunction = async function (this: Service, secretName: string, value: string) { + return this.wrapInSpan(PuristaSpanName.SecretStoreGetValue, {}, async span => { + try { + span.setAttributes({ + [PuristaSpanTag.StoreName]: this.secretStore.name, + [PuristaSpanTag.StoreType]: StoreType.SecretStore, + }) + return this.secretStore.setSecret(secretName, value) + } catch (err) { + span.recordException(err as Error) + throw err + } + }) + } + const setSecret: SecretSetterFunction = setSecretFunction.bind(this) + + const removeSecretFunction = async function (this: Service, secretName: string) { + return this.wrapInSpan(PuristaSpanName.SecretStoreGetValue, {}, async span => { + try { + span.setAttributes({ + [PuristaSpanTag.StoreName]: this.secretStore.name, + [PuristaSpanTag.StoreType]: StoreType.SecretStore, + }) + return this.secretStore.removeSecret(secretName) + } catch (err) { + span.recordException(err as Error) + throw err + } + }) + } + const removeSecret: SecretDeleteFunction = removeSecretFunction.bind(this) + + const getConfigFunction = async function (this: Service, ...configNames: string[]) { + return this.wrapInSpan(PuristaSpanName.ConfigStoreGetValue, {}, async span => { + try { + span.setAttributes({ + [PuristaSpanTag.StoreName]: this.configStore.name, + [PuristaSpanTag.StoreType]: StoreType.ConfigStore, + }) + return this.configStore.getConfig(...configNames) + } catch (err) { + span.recordException(err as Error) + throw err + } + }) + } + const getConfig: ConfigGetterFunction = getConfigFunction.bind(this) + + const setConfigFunction = async function (this: Service, configName: string, value: unknown) { + return this.wrapInSpan(PuristaSpanName.ConfigStoreGetValue, {}, async span => { + try { + span.setAttributes({ + [PuristaSpanTag.StoreName]: this.configStore.name, + [PuristaSpanTag.StoreType]: StoreType.ConfigStore, + }) + return this.configStore.setConfig(configName, value) + } catch (err) { + span.recordException(err as Error) + throw err + } + }) + } + const setConfig: ConfigSetterFunction = setConfigFunction.bind(this) + + const removeConfigFunction = async function (this: Service, configName: string) { + return this.wrapInSpan(PuristaSpanName.ConfigStoreGetValue, {}, async span => { + try { + span.setAttributes({ + [PuristaSpanTag.StoreName]: this.configStore.name, + [PuristaSpanTag.StoreType]: StoreType.ConfigStore, + }) + return this.configStore.removeConfig(configName) + } catch (err) { + span.recordException(err as Error) + throw err + } + }) + } + const removeConfig: ConfigDeleteFunction = removeConfigFunction.bind(this) + + const getStateFunction = async function (this: Service, ...stateNames: string[]) { + return this.wrapInSpan(PuristaSpanName.StateStoreGetValue, {}, async span => { + try { + span.setAttributes({ + [PuristaSpanTag.StoreName]: this.stateStore.name, + [PuristaSpanTag.StoreType]: StoreType.StateStore, + }) + return this.stateStore.getState(...stateNames) + } catch (err) { + span.recordException(err as Error) + throw err + } + }) + } + const getState: StateGetterFunction = getStateFunction.bind(this) + + const setStateFunction = async function (this: Service, stateName: string, value: unknown) { + return this.wrapInSpan(PuristaSpanName.StateStoreGetValue, {}, async span => { + try { + span.setAttributes({ + [PuristaSpanTag.StoreName]: this.stateStore.name, + [PuristaSpanTag.StoreType]: StoreType.StateStore, + }) + return this.stateStore.setState(stateName, value) + } catch (err) { + span.recordException(err as Error) + throw err + } + }) + } + const setState: StateSetterFunction = setStateFunction.bind(this) + + const removeStateFunction = async function (this: Service, stateName: string) { + return this.wrapInSpan(PuristaSpanName.StateStoreGetValue, {}, async span => { + try { + span.setAttributes({ + [PuristaSpanTag.StoreName]: this.stateStore.name, + [PuristaSpanTag.StoreType]: StoreType.StateStore, + }) + return this.stateStore.removeState(stateName) + } catch (err) { + span.recordException(err as Error) + throw err + } + }) + } + const removeState: StateDeleteFunction = removeStateFunction.bind(this) + + return { + logger, + wrapInSpan: this.wrapInSpan.bind(this), + startActiveSpan: this.startActiveSpan.bind(this), + secrets: { + getSecret, + setSecret, + removeSecret, + }, + configs: { + getConfig, + setConfig, + removeConfig, + }, + states: { + getState, + setState, + removeState, + }, + } + } + + /** + * Called when a command is received by the service + * + * @param subscriptionId + * @param command + */ + public async executeCommand(message: Readonly) { + const command = this.commands.get(message.receiver.serviceTarget) + + const context = deserializeOtp(this.logger, message.otp) + + return this.startActiveSpan(command?.commandName ?? 'purista.executeCommand', {}, context, async span => { + const traceId = message.traceId + + const logger = this.logger.getChildLogger({ + serviceTarget: command?.commandName, + ...span.spanContext(), + customTraceId: traceId, + principalId: message.principalId, + tenantId: message.tenantId, + }) + + if (message.principalId) { + span.setAttribute(PuristaSpanTag.PrincipalId, message.principalId) + } + + if (message.tenantId) { + span.setAttribute(PuristaSpanTag.TenantId, message.tenantId) + } + + if (!command) { + logger.error({ message: getCleanedMessage(message) }, 'received invalid command') + + span.setStatus({ + code: SpanStatusCode.ERROR, + message: 'received invalid command', + }) + return await this.startActiveSpan('sendErrorResponse', {}, undefined, async () => + createErrorResponse(this.eventBridge.instanceId, message, StatusCode.NotImplemented), + ) + } + + try { + const { payload, parameter } = await commandTransformInput(this, logger, command, message) + + let result = await this.startActiveSpan( + `${command.commandName}.functionExecution`, + {}, + undefined, + async _subSpan => { + const context: CommandFunctionContext = { + message, + emit: this.getEmitFunction( + command.commandName, + traceId, + message.principalId, + message.tenantId, + command.emitList, + ), + ...this.getContextFunctions(logger), + service: createInvokeFunctionProxy( + this.getInvokeFunction( + command.commandName, + traceId, + message.principalId, + message.tenantId, + command.invokes, + ), + ), + resources: this.resources, + } + const call = command.call.bind(this, context) + return (await call(payload as Readonly, parameter as Readonly)) as unknown + }, + ) + + if (Object.keys(command.hooks.afterGuard ?? {}).length) { + const guards = command.hooks.afterGuard + + await this.startActiveSpan(`${command.commandName}.afterGuardHooks`, {}, undefined, async () => { + const guardsPromises: Promise[] = [] + + for (const [name, hook] of Object.entries(guards ?? {})) { + const context: CommandFunctionContext = { + message, + emit: this.getEmitFunction( + command.commandName, + traceId, + message.principalId, + message.tenantId, + command.emitList, + ), + ...this.getContextFunctions(logger), + service: createInvokeFunctionProxy( + this.getInvokeFunction( + command.commandName, + traceId, + message.principalId, + message.tenantId, + command.invokes, + ), + ), + resources: this.resources, + } + + const guardPromise = this.wrapInSpan(`afterGuardHook.${name}`, {}, async _subSpan => { + return hook.bind(this)( + context, + result as Readonly, + payload as Readonly, + parameter as Readonly, + ) + }) + guardsPromises.push(guardPromise) + } + + await Promise.all(guardsPromises) + }) + } + + if (command.hooks.transformOutput) { + const transformOutput = command.hooks.transformOutput + await this.startActiveSpan(`${command.commandName}.outputTransformation`, {}, undefined, async subSpan => { + const afterTransform = transformOutput.transformFunction.bind(this, { + message, + ...this.getContextFunctions(logger), + resources: this.resources, + }) + const resultTransformed = await afterTransform( + result as Readonly, + parameter as Readonly, + ) + + const validationResult = await validate(transformOutput.transformOutputSchema, resultTransformed) + if (!validationResult.success) { + const err = new UnhandledError(StatusCode.InternalServerError, undefined, validationResult.issues) + subSpan.recordException(err) + logger.warn({ ...subSpan.spanContext(), err }, 'transform output validation failed:', err.message) + + subSpan.setStatus({ + code: SpanStatusCode.ERROR, + message: 'transform output validation failed', + }) + throw err + } + result = validationResult.data as unknown + }) + } + + return await this.startActiveSpan(`${command.commandName}.success`, {}, undefined, async subSpan => { + if (command.eventName) { + subSpan.addEvent(command.eventName) + this.emit(`custom-${command.eventName}`, result) + } + return { + ...createSuccessResponse(this.eventBridge.instanceId, message, result, command.eventName), + otp: serializeOtp(), + } + }) + } catch (error) { + span.recordException(error as Error) + + if (error instanceof HandledError) { + this.emit(ServiceEventsNames.CommandHandledError, { + commandName: command.commandName, + error, + traceId, + }) + span.setStatus({ + code: SpanStatusCode.ERROR, + message: error.message, + }) + + return await this.startActiveSpan('sendErrorResponse', {}, undefined, async () => + createErrorResponse(this.eventBridge.instanceId, message, error.errorCode, error), + ) + } + + this.emit(ServiceEventsNames.CommandUnhandledError, { + commandName: command.commandName, + error, + traceId, + }) + + logger.error( + { err: error, message: getCleanedMessage(message), ...span.spanContext() }, + 'executeCommand unhandled error', + ) + + span.setStatus({ + code: SpanStatusCode.ERROR, + message: 'executeCommand unhandled error', + }) + + return await this.startActiveSpan(`${command.commandName}.error`, {}, undefined, async () => + createErrorResponse(this.eventBridge.instanceId, message, StatusCode.InternalServerError, error), + ) + } + }) + } + + public async registerCommand( + commandDefinition: CommandDefinition< + any, + any, + any, + any, + any, + any, + any, + any, + any, + any, + S['Resources'], + any, + any, + any + >, + ): Promise { + return this.startActiveSpan('purista.registerCommand', {}, undefined, async span => { + this.logger.debug({ ...this.serviceInfo, ...span.spanContext() }, 'register command') + + span.setAttributes({ + serviceName: this.serviceInfo.serviceName, + serviceVersion: this.serviceInfo.serviceVersion, + commandName: commandDefinition.commandName, + }) + + this.commands.set(commandDefinition.commandName, commandDefinition) + + await this.eventBridge.registerCommand( + { + serviceName: this.serviceInfo.serviceName, + serviceVersion: this.serviceInfo.serviceVersion, + serviceTarget: commandDefinition.commandName, + }, + this.executeCommand.bind(this), + commandDefinition.metadata, + commandDefinition.eventBridgeConfig, + ) + + span.end() + }) + } + + public async executeSubscription( + message: Readonly, + subscriptionName: string, + ): Promise | undefined> { + const subscription = this.subscriptions.get(subscriptionName) + + const otpContext = deserializeOtp(this.logger, message.otp) + const spanContext = otpContext ? trace.getSpanContext(otpContext) : undefined + + return this.startActiveSpan( + subscriptionName || 'purista.executeSubscription', + { links: spanContext ? [{ context: spanContext }] : [] }, + undefined, + async span => { + const traceId = message.traceId + + const logger = this.logger.getChildLogger({ + serviceTarget: subscriptionName, + ...span.spanContext(), + customTraceId: traceId, + principalId: message.principalId, + tenantId: message.tenantId, + }) + + if (message.principalId) { + span.setAttribute(PuristaSpanTag.PrincipalId, message.principalId) + } + + if (message.tenantId) { + span.setAttribute(PuristaSpanTag.TenantId, message.tenantId) + } + + if (!subscription) { + logger.error({ message: getCleanedMessage(message) }, 'received message for invalid subscription') + + span.setStatus({ + code: SpanStatusCode.ERROR, + message: 'received message for invalid subscription', + }) + return + } + + try { + const { payload, parameter } = await subscriptionTransformInput(this, logger, subscription, message) + + let result: unknown = await this.startActiveSpan( + `${subscription.subscriptionName}.functionExecution`, + {}, + undefined, + async _subSpan => { + const context: SubscriptionFunctionContext = { + message, + emit: this.getEmitFunction( + subscriptionName, + traceId, + message.principalId, + message.tenantId, + subscription.emitList, + ), + ...this.getContextFunctions(logger), + service: createInvokeFunctionProxy( + this.getInvokeFunction( + subscriptionName, + traceId, + message.principalId, + message.tenantId, + subscription.invokes, + ), + ), + resources: this.resources, + } + const call2 = subscription.call.bind(this, context) + return await call2(payload, parameter) + }, + ) + + if (Object.keys(subscription.hooks.afterGuard ?? {}).length) { + const guards = subscription.hooks.afterGuard + + await this.startActiveSpan(`${subscription.subscriptionName}.afterGuardHooks`, {}, undefined, async () => { + const guardsPromises: Promise[] = [] + + for (const [name, hook] of Object.entries(guards ?? {})) { + const context: SubscriptionFunctionContext = { + message, + emit: this.getEmitFunction( + subscription.subscriptionName, + traceId, + message.principalId, + message.tenantId, + subscription.emitList, + ), + ...this.getContextFunctions(logger), + service: createInvokeFunctionProxy( + this.getInvokeFunction( + subscription.subscriptionName, + traceId, + message.principalId, + message.tenantId, + ), + ), + resources: this.resources, + } + + const guardPromise = this.wrapInSpan(`afterGuardHook.${name}`, {}, async _subSpan => { + return hook.bind(this)(context, result as Readonly, payload, parameter) + }) + guardsPromises.push(guardPromise) + } + + await Promise.all(guardsPromises) + }) + } + + if (subscription.hooks.transformOutput) { + const transformOutput = subscription.hooks.transformOutput + await this.startActiveSpan( + `${subscription.subscriptionName}.outputTransformation`, + {}, + undefined, + async subSpan => { + const afterTransform = transformOutput.transformFunction.bind(this, { + message, + ...this.getContextFunctions(logger), + resources: this.resources, + }) + const resultTransformed = await afterTransform(result as Readonly, parameter) + + const validationResult = await validate(transformOutput.transformOutputSchema, resultTransformed) + if (!validationResult.success) { + const err = new UnhandledError(StatusCode.InternalServerError, undefined, validationResult.issues) + subSpan.recordException(err) + logger.warn({ ...subSpan.spanContext(), err }, 'transform output validation failed:', err.message) + + subSpan.setStatus({ + code: SpanStatusCode.ERROR, + message: 'transform output validation failed', + }) + throw err + } + result = validationResult.data + }, + ) + } + + if (subscription.emitEventName) { + return await this.startActiveSpan( + `${subscription.subscriptionName}.success`, + {}, + undefined, + async subSpan => { + this.emit(`custom-${subscription.emitEventName}`, result) + subSpan.addEvent(subscription.emitEventName as string) + const resultMsg: Omit = { + messageType: EBMessageType.CustomMessage, + contentType: subscription.metadata.expose.contentTypeResponse ?? 'application/json', + contentEncoding: subscription.metadata.expose.contentEncodingResponse ?? 'utf-8', + sender: { + serviceName: this.serviceInfo.serviceName, + serviceVersion: this.serviceInfo.serviceVersion, + serviceTarget: subscription.subscriptionName, + instanceId: this.eventBridge.instanceId, + }, + payload: result, + eventName: subscription.emitEventName as string, + otp: serializeOtp(), + } + return resultMsg + }, + ) + } + return undefined + } catch (err) { + logger.error({ err }, 'Error in subscription execution') + if (err instanceof HandledError) { + this.emit(ServiceEventsNames.SubscriptionHandledError, { + subscriptionName, + error: err, + traceId, + }) + // handled errors prevent that the message is re-delivered for retry + return + } + if (err instanceof UnhandledError) { + this.emit(ServiceEventsNames.SubscriptionUnhandledError, { + subscriptionName, + error: err, + traceId, + }) + } + span.recordException(err as Error) + + span.setStatus({ + code: SpanStatusCode.ERROR, + message: (err as Error).message, + }) + + // re-throw error here, so the underlaying event bridge driver can handle ack/re-delivery for retry + throw err + } + }, + ) + } + + public async registerSubscription( + subscriptionDefinition: SubscriptionDefinition< + any, + any, + any, + any, + any, + any, + any, + any, + S['Resources'], + any, + any, + any + >, + ): Promise { + return this.startActiveSpan('purista.registerSubscription', {}, undefined, async span => { + this.logger.debug({ ...this.serviceInfo, ...span.spanContext() }, 'register subscription') + + span.setAttributes({ + serviceName: this.info.serviceName, + serviceVersion: this.info.serviceVersion, + subscriptionName: subscriptionDefinition.subscriptionName, + }) + + this.subscriptions.set(subscriptionDefinition.subscriptionName, subscriptionDefinition) + + const subscription: Subscription = { + sender: subscriptionDefinition.sender, + receiver: subscriptionDefinition.receiver, + messageType: subscriptionDefinition.messageType, + eventName: subscriptionDefinition.eventName, + emitEventName: subscriptionDefinition.emitEventName, + subscriber: { + serviceName: this.info.serviceName, + serviceVersion: this.info.serviceVersion, + serviceTarget: subscriptionDefinition.subscriptionName, + }, + eventBridgeConfig: subscriptionDefinition.eventBridgeConfig, + principalId: subscriptionDefinition.principalId, + tenantId: subscriptionDefinition.tenantId, + } + + await this.eventBridge.registerSubscription(subscription, (message: EBMessage) => + this.executeSubscription(message, subscriptionDefinition.subscriptionName), + ) + + span.end() + }) + } + + async destroy() { + this.emit(ServiceEventsNames.ServiceDrain) + this.emit(ServiceEventsNames.ServiceStopped) + this.removeAllListeners() + await super.destroy() + } } diff --git a/packages/core/src/core/Service/ServiceBaseClass/ServiceBaseClass.impl.ts b/packages/core/src/core/Service/ServiceBaseClass/ServiceBaseClass.impl.ts index 8ed8a1334..2e6abe6b1 100644 --- a/packages/core/src/core/Service/ServiceBaseClass/ServiceBaseClass.impl.ts +++ b/packages/core/src/core/Service/ServiceBaseClass/ServiceBaseClass.impl.ts @@ -1,9 +1,10 @@ import type { Context, Span, SpanOptions } from '@opentelemetry/api' + import { SpanStatusCode } from '@opentelemetry/api' import { Resource } from '@opentelemetry/resources' import type { SpanProcessor } from '@opentelemetry/sdk-trace-node' import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node' -import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions' +import { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from '@opentelemetry/semantic-conventions' import type { Schema } from '@typeschema/main' import { puristaVersion } from '../../../version.js' @@ -25,178 +26,178 @@ import { ServiceInfoValidator } from '../ServiceInfoValidator.impl.js' * @group Service */ export class ServiceBaseClass extends GenericEventEmitter { - readonly info: ServiceInfoType - - protected eventBridge: EventBridge - - protected logger: Logger - - spanProcessor: SpanProcessor | undefined - - traceProvider: NodeTracerProvider - - protected secretStore: SecretStore - protected configStore: ConfigStore - protected stateStore: StateStore - - protected configSchema: Schema | undefined - - constructor(options: { - logger: Logger - info: ServiceInfoType - eventBridge: EventBridge - spanProcessor?: SpanProcessor - secretStore: SecretStore - configStore: ConfigStore - stateStore: StateStore - configSchema?: Schema - }) { - super() - this.info = new Proxy( - { - serviceName: '', - serviceDescription: '', - serviceVersion: '1', - }, - ServiceInfoValidator, - ) - - this.info.serviceDescription = options.info.serviceDescription - this.info.serviceName = options.info.serviceName - this.info.serviceVersion = options.info.serviceVersion - - this.logger = options.logger.getChildLogger({ - serviceName: this.info.serviceName, - serviceVersion: this.info.serviceVersion, - puristaVersion, - }) - this.logger.debug({ ...this.info }, `creating ${this.info.serviceName} ${this.info.serviceVersion}`) - - const resource = Resource.default().merge( - new Resource({ - [SemanticResourceAttributes.SERVICE_NAME]: this.info.serviceName, - [SemanticResourceAttributes.SERVICE_VERSION]: this.info.serviceVersion, - }), - ) - this.traceProvider = new NodeTracerProvider({ - resource, - }) - - if (options.spanProcessor) { - this.traceProvider.addSpanProcessor(options.spanProcessor) - } - - this.traceProvider.register() - this.spanProcessor = options.spanProcessor - - this.eventBridge = options.eventBridge - - this.secretStore = options.secretStore - this.configStore = options.configStore - this.stateStore = options.stateStore - } - - /** - * Get service info - */ - get serviceInfo(): ServiceInfoType { - return Object.freeze({ ...this.info }) - } - - /** - * Returns open telemetry tracer of this service - * - * @returns Tracer - */ - getTracer(name?: string, version?: string) { - return this.traceProvider.getTracer( - name ?? this.serviceInfo.serviceName, - version ?? this.serviceInfo.serviceVersion, - ) - } - - /** - * Start a child span for opentelemetry tracking - * @param name name of span - * @param opts span options - * @param context optional context - * @param fn function to be executed within the span - * @returns return value of fn - */ - async startActiveSpan( - name: string, - opts: SpanOptions, - context: Context | undefined = undefined, - fn: (span: Span) => Promise, - ): Promise { - const tracer = this.getTracer() - - const callback = async (span: Span) => { - span.setAttribute(PuristaSpanTag.PuristaVersion, puristaVersion) - try { - return await fn(span) - } catch (error) { - let message = 'error' - if (error instanceof Error) { - message = error.message - } - - span.recordException(error as Error) - span.setStatus({ - code: SpanStatusCode.ERROR, - message, - }) - - throw error - } finally { - span.end() - } - } - - return context - ? tracer.startActiveSpan(name, opts, context, callback) - : tracer.startActiveSpan(name, opts, callback) - } - - /** - * Start span for opentelemetry tracking on same level. - * The created span will not become the "active" span within opentelemetry! - * - * This means during logging and similar the spanId of parent span is logged. - * - * Use wrapInSpan for marking points in flow of one bigger function, - * but not to trace the program flow itself - * - * @param name name of span - * @param opts span options - * @param fn function te be executed in the span - * @param context span context - * @returns return value of fn - */ - async wrapInSpan(name: string, opts: SpanOptions, fn: (span: Span) => Promise, context?: Context): Promise { - const tracer = this.getTracer() - const span = tracer.startSpan(name, opts, context) - span.setAttribute(PuristaSpanTag.PuristaVersion, puristaVersion) - try { - return await fn(span) - } catch (error) { - let message = 'error' - if (error instanceof Error) { - message = error.message - } - span.recordException(error as Error) - span.setStatus({ - code: SpanStatusCode.ERROR, - message, - }) - - throw error - } finally { - span.end() - } - } - - async destroy() { - this.logger.info({ ...this.info }, 'stopped') - } + readonly info: ServiceInfoType + + protected eventBridge: EventBridge + + public logger: Logger + + spanProcessor: SpanProcessor | undefined + + traceProvider: NodeTracerProvider + + protected secretStore: SecretStore + protected configStore: ConfigStore + protected stateStore: StateStore + + protected configSchema: Schema | undefined + + constructor(options: { + logger: Logger + info: ServiceInfoType + eventBridge: EventBridge + spanProcessor?: SpanProcessor + secretStore: SecretStore + configStore: ConfigStore + stateStore: StateStore + configSchema?: Schema + }) { + super() + this.info = new Proxy( + { + serviceName: '', + serviceDescription: '', + serviceVersion: '1', + }, + ServiceInfoValidator, + ) + + this.info.serviceDescription = options.info.serviceDescription + this.info.serviceName = options.info.serviceName + this.info.serviceVersion = options.info.serviceVersion + + this.logger = options.logger.getChildLogger({ + serviceName: this.info.serviceName, + serviceVersion: this.info.serviceVersion, + puristaVersion, + }) + this.logger.debug({ ...this.info }, `creating ${this.info.serviceName} ${this.info.serviceVersion}`) + + const resource = Resource.default().merge( + new Resource({ + [ATTR_SERVICE_NAME]: this.info.serviceName, + [ATTR_SERVICE_VERSION]: this.info.serviceVersion, + }), + ) + this.traceProvider = new NodeTracerProvider({ + resource, + }) + + if (options.spanProcessor) { + this.traceProvider.addSpanProcessor(options.spanProcessor) + } + + this.traceProvider.register() + this.spanProcessor = options.spanProcessor + + this.eventBridge = options.eventBridge + + this.secretStore = options.secretStore + this.configStore = options.configStore + this.stateStore = options.stateStore + } + + /** + * Get service info + */ + get serviceInfo(): ServiceInfoType { + return Object.freeze({ ...this.info }) + } + + /** + * Returns open telemetry tracer of this service + * + * @returns Tracer + */ + getTracer(name?: string, version?: string) { + return this.traceProvider.getTracer( + name ?? this.serviceInfo.serviceName, + version ?? this.serviceInfo.serviceVersion, + ) + } + + /** + * Start a child span for opentelemetry tracking + * @param name name of span + * @param opts span options + * @param context optional context + * @param fn function to be executed within the span + * @returns return value of fn + */ + async startActiveSpan( + name: string, + opts: SpanOptions, + context: Context | undefined, + fn: (span: Span) => Promise, + ): Promise { + const tracer = this.getTracer() + + const callback = async (span: Span) => { + span.setAttribute(PuristaSpanTag.PuristaVersion, puristaVersion) + try { + return await fn(span) + } catch (error) { + let message = 'error' + if (error instanceof Error) { + message = error.message + } + + span.recordException(error as Error) + span.setStatus({ + code: SpanStatusCode.ERROR, + message, + }) + + throw error + } finally { + span.end() + } + } + + return context + ? tracer.startActiveSpan(name, opts, context, callback) + : tracer.startActiveSpan(name, opts, callback) + } + + /** + * Start span for opentelemetry tracking on same level. + * The created span will not become the "active" span within opentelemetry! + * + * This means during logging and similar the spanId of parent span is logged. + * + * Use wrapInSpan for marking points in flow of one bigger function, + * but not to trace the program flow itself + * + * @param name name of span + * @param opts span options + * @param fn function te be executed in the span + * @param context span context + * @returns return value of fn + */ + async wrapInSpan(name: string, opts: SpanOptions, fn: (span: Span) => Promise, context?: Context): Promise { + const tracer = this.getTracer() + const span = tracer.startSpan(name, opts, context) + span.setAttribute(PuristaSpanTag.PuristaVersion, puristaVersion) + try { + return await fn(span) + } catch (error) { + let message = 'error' + if (error instanceof Error) { + message = error.message + } + span.recordException(error as Error) + span.setStatus({ + code: SpanStatusCode.ERROR, + message, + }) + + throw error + } finally { + span.end() + } + } + + async destroy() { + this.logger.info({ ...this.info }, 'stopped') + } } diff --git a/packages/core/src/core/Service/ServiceBaseClass/ServiceInfoValidator.impl.ts b/packages/core/src/core/Service/ServiceBaseClass/ServiceInfoValidator.impl.ts index 89a08547a..c6f9b5b31 100644 --- a/packages/core/src/core/Service/ServiceBaseClass/ServiceInfoValidator.impl.ts +++ b/packages/core/src/core/Service/ServiceBaseClass/ServiceInfoValidator.impl.ts @@ -3,28 +3,28 @@ import type { ServiceInfoType } from '../../types/index.js' const serviceNameRegex = /^[a-zA-Z0-9-_]*$/ const serviceVersionRegex = /^\d+$/ export const ServiceInfoValidator = { - set(obj: ServiceInfoType, prop: keyof ServiceInfoType, value: string) { - if (prop === 'serviceName') { - if (!value.length) { - throw new TypeError('serviceName must be set') - } - if (!value.match(serviceNameRegex)) { - throw new TypeError( - `serviceName "${value}" is invalid. Only allowed to have a-z, A-Z, 0-9, -, _ as characters.`, - ) - } - } + set(obj: ServiceInfoType, prop: keyof ServiceInfoType, value: string) { + if (prop === 'serviceName') { + if (!value.length) { + throw new TypeError('serviceName must be set') + } + if (!value.match(serviceNameRegex)) { + throw new TypeError( + `serviceName "${value}" is invalid. Only allowed to have a-z, A-Z, 0-9, -, _ as characters.`, + ) + } + } - if (prop === 'serviceVersion') { - if (!value.length) { - throw new TypeError('serviceVersion must be set') - } - if (!value.match(serviceVersionRegex)) { - throw new TypeError(`serviceVersion "${value}" is invalid. Only allowed number characters 0-9.`) - } - } + if (prop === 'serviceVersion') { + if (!value.length) { + throw new TypeError('serviceVersion must be set') + } + if (!value.match(serviceVersionRegex)) { + throw new TypeError(`serviceVersion "${value}" is invalid. Only allowed number characters 0-9.`) + } + } - obj[prop] = value - return true - }, + obj[prop] = value + return true + }, } diff --git a/packages/core/src/core/Service/ServiceInfoValidator.impl.ts b/packages/core/src/core/Service/ServiceInfoValidator.impl.ts index 1af31cb89..9a64e9821 100644 --- a/packages/core/src/core/Service/ServiceInfoValidator.impl.ts +++ b/packages/core/src/core/Service/ServiceInfoValidator.impl.ts @@ -3,28 +3,28 @@ import type { ServiceInfoType } from '../types/index.js' const serviceNameRegex = /^[a-zA-Z0-9-_]+$/ const serviceVersionRegex = /^\d+$/ export const ServiceInfoValidator = { - set(obj: ServiceInfoType, prop: keyof ServiceInfoType, value: string) { - if (prop === 'serviceName') { - if (!value.length) { - throw new TypeError('serviceName must be set') - } - if (!value.match(serviceNameRegex)) { - throw new TypeError( - `serviceName "${value}" is invalid. Only allowed to have a-z, A-Z, 0-9, -, _ as characters.`, - ) - } - } + set(obj: ServiceInfoType, prop: keyof ServiceInfoType, value: string) { + if (prop === 'serviceName') { + if (!value.length) { + throw new TypeError('serviceName must be set') + } + if (!value.match(serviceNameRegex)) { + throw new TypeError( + `serviceName "${value}" is invalid. Only allowed to have a-z, A-Z, 0-9, -, _ as characters.`, + ) + } + } - if (prop === 'serviceVersion') { - if (!value.length) { - throw new TypeError('serviceVersion must be set') - } - if (!value.match(serviceVersionRegex)) { - throw new TypeError(`serviceVersion "${value}" is invalid. Only allowed number characters 0-9.`) - } - } + if (prop === 'serviceVersion') { + if (!value.length) { + throw new TypeError('serviceVersion must be set') + } + if (!value.match(serviceVersionRegex)) { + throw new TypeError(`serviceVersion "${value}" is invalid. Only allowed number characters 0-9.`) + } + } - obj[prop] = value - return true - }, + obj[prop] = value + return true + }, } diff --git a/packages/core/src/core/Service/commandTransformInput.impl.ts b/packages/core/src/core/Service/commandTransformInput.impl.ts index d84092f78..20569ce87 100644 --- a/packages/core/src/core/Service/commandTransformInput.impl.ts +++ b/packages/core/src/core/Service/commandTransformInput.impl.ts @@ -6,89 +6,84 @@ import type { Command, CommandDefinition, Logger } from '../types/index.js' import { StatusCode } from '../types/index.js' import type { Service } from './Service.impl.js' -export const commandTransformInput = async ( - serviceInstance: Service, - logger: Logger, - command: CommandDefinition, - message: Readonly>, -): Promise<{ payload: Readonly; parameter: Readonly }> => { - if (!command.hooks.transformInput) { - return message.payload as { payload: Readonly; parameter: Readonly } - } +export const commandTransformInput = async ( + serviceInstance: S, + logger: Logger, + command: CommandDefinition, + message: Readonly>, +) => { + if (!command.hooks.transformInput) { + return message.payload as Readonly + } - const transformInput = command.hooks.transformInput - return await serviceInstance.startActiveSpan( - command.commandName + '.inputTransformation', - {}, - undefined, - async (_) => { - const transform = transformInput.transformFunction.bind(serviceInstance, { - message, - ...serviceInstance.getContextFunctions(logger), - }) - const parameterInput = await serviceInstance.wrapInSpan( - command.commandName + '.validateParameter', - {}, - async (subSpan) => { - const validationResult = await validate(transformInput.transformParameterSchema, message.payload.parameter) - if (validationResult.success) { - return validationResult.data as Readonly - } - const err = new HandledError(StatusCode.BadRequest, undefined, validationResult.issues) - subSpan.recordException(err) - logger.warn( - { ...subSpan.spanContext(), err }, - 'transform input validation for parameters failed:', - err.message, - ) + const transformInput = command.hooks.transformInput + return await serviceInstance.startActiveSpan(`${command.commandName}.inputTransformation`, {}, undefined, async _ => { + const transform = transformInput.transformFunction.bind(serviceInstance, { + message, + ...serviceInstance.getContextFunctions(logger), + resources: serviceInstance.resources, + }) + const parameterInput = await serviceInstance.wrapInSpan( + `${command.commandName}.validateParameter`, + {}, + async subSpan => { + const validationResult = await validate(transformInput.transformParameterSchema, message.payload.parameter) + if (validationResult.success) { + return validationResult.data + } + const err = new HandledError(StatusCode.BadRequest, undefined, validationResult.issues) + subSpan.recordException(err) + logger.warn({ ...subSpan.spanContext(), err }, 'transform input validation for parameters failed:', err.message) - subSpan.setStatus({ - code: SpanStatusCode.ERROR, - message: 'transform input validation for parameters failed', - }) - throw err - }, - ) + subSpan.setStatus({ + code: SpanStatusCode.ERROR, + message: 'transform input validation for parameters failed', + }) + throw err + }, + ) - const payloadInput = await serviceInstance.wrapInSpan( - command.commandName + '.validatePayload', - {}, - async (subSpan) => { - const validationResult = await validate(transformInput.transformInputSchema, message.payload.payload) - if (validationResult.success) { - return validationResult.data as Readonly - } - const err = new HandledError(StatusCode.BadRequest, undefined, validationResult.issues) - subSpan.recordException(err) - logger.warn({ ...subSpan.spanContext(), err }, 'transform input validation for payload failed:', err.message) + const payloadInput = await serviceInstance.wrapInSpan( + `${command.commandName}.validatePayload`, + {}, + async subSpan => { + const validationResult = await validate(transformInput.transformInputSchema, message.payload.payload) + if (validationResult.success) { + return validationResult.data + } + const err = new HandledError(StatusCode.BadRequest, undefined, validationResult.issues) + subSpan.recordException(err) + logger.warn({ ...subSpan.spanContext(), err }, 'transform input validation for payload failed:', err.message) - subSpan.setStatus({ - code: SpanStatusCode.ERROR, - message: 'transform input validation for payload failed', - }) - throw err - }, - ) + subSpan.setStatus({ + code: SpanStatusCode.ERROR, + message: 'transform input validation for payload failed', + }) + throw err + }, + ) - return await serviceInstance.wrapInSpan(command.commandName + '.transformFunction', {}, async (subSpan) => { - try { - return await transform(payloadInput, parameterInput) - } catch (error) { - const err = error as Error - subSpan.recordException(err) - subSpan.setStatus({ - code: SpanStatusCode.ERROR, - message: err.message || 'Unable to transform input', - }) + return await serviceInstance.wrapInSpan(`${command.commandName}.transformFunction`, {}, async subSpan => { + try { + return await transform( + payloadInput as Readonly, + parameterInput as Readonly, + ) + } catch (error) { + const err = error as Error + subSpan.recordException(err) + subSpan.setStatus({ + code: SpanStatusCode.ERROR, + message: err.message || 'Unable to transform input', + }) - if (error instanceof HandledError) { - throw error - } - logger.error({ err, ...subSpan.spanContext() }, 'Unable to transform input:') + if (error instanceof HandledError) { + throw error + } + logger.error({ err, ...subSpan.spanContext() }, 'Unable to transform input:') - throw new UnhandledError(StatusCode.InternalServerError, 'Unable to transform input') - } - }) - }, - ) + throw new UnhandledError(StatusCode.InternalServerError, 'Unable to transform input') + } + }) + }) } diff --git a/packages/core/src/core/Service/commandTransformInput.test.ts b/packages/core/src/core/Service/commandTransformInput.test.ts index c2336dfcc..26bababb2 100644 --- a/packages/core/src/core/Service/commandTransformInput.test.ts +++ b/packages/core/src/core/Service/commandTransformInput.test.ts @@ -1,5 +1,5 @@ describe('commandTransformInput', () => { - it.todo('throws HandledError if parameter schema validation fails') - it.todo('throws HandledError if payload schema validation fails') - it.todo('throws HandledError if output schema validation fails') + it.todo('throws HandledError if parameter schema validation fails') + it.todo('throws HandledError if payload schema validation fails') + it.todo('throws HandledError if output schema validation fails') }) diff --git a/packages/core/src/core/Service/service.test.ts b/packages/core/src/core/Service/service.test.ts index 479987323..d8c84dadf 100644 --- a/packages/core/src/core/Service/service.test.ts +++ b/packages/core/src/core/Service/service.test.ts @@ -3,27 +3,27 @@ import type { ServiceInfoType } from '../types/index.js' import { Service } from './Service.impl.js' describe('Service', () => { - const serviceInfo: ServiceInfoType = { - serviceName: 'TestService', - serviceVersion: '1', - serviceDescription: 'A service for unit tests', - } + const serviceInfo: ServiceInfoType = { + serviceName: 'TestService', + serviceVersion: '1', + serviceDescription: 'A service for unit tests', + } - it('creates a new instance', async () => { - const logger = getLoggerMock().mock - const eventBridge = getEventBridgeMock().mock + it('creates a new instance', async () => { + const logger = getLoggerMock().mock + const eventBridge = getEventBridgeMock().mock - const service = new Service({ - logger, - eventBridge, - info: serviceInfo, - commandDefinitionList: [], - subscriptionDefinitionList: [], - config: {}, - }) + const service = new Service({ + logger, + eventBridge, + info: serviceInfo, + commandDefinitionList: [], + subscriptionDefinitionList: [], + config: {}, + }) - await expect(service.start()).resolves.toBeUndefined() + await expect(service.start()).resolves.toBeUndefined() - await expect(service.destroy()).resolves.toBeUndefined() - }) + await expect(service.destroy()).resolves.toBeUndefined() + }) }) diff --git a/packages/core/src/core/Service/serviceInfoValidator.test.ts b/packages/core/src/core/Service/serviceInfoValidator.test.ts index 455669209..a24bbf8e5 100644 --- a/packages/core/src/core/Service/serviceInfoValidator.test.ts +++ b/packages/core/src/core/Service/serviceInfoValidator.test.ts @@ -1,59 +1,59 @@ import { ServiceInfoValidator } from './ServiceInfoValidator.impl.js' describe('ServiceInfoValidator', () => { - it('throws on empty service name', () => { - const info = new Proxy( - { - serviceName: '', - serviceDescription: '', - serviceVersion: '1', - }, - ServiceInfoValidator, - ) - expect(() => { - info.serviceName = '' - }).toThrow('serviceName must be set') - }) + it('throws on empty service name', () => { + const info = new Proxy( + { + serviceName: '', + serviceDescription: '', + serviceVersion: '1', + }, + ServiceInfoValidator, + ) + expect(() => { + info.serviceName = '' + }).toThrow('serviceName must be set') + }) - it('throws on invalid service name', () => { - const info = new Proxy( - { - serviceName: '', - serviceDescription: '', - serviceVersion: '1', - }, - ServiceInfoValidator, - ) - expect(() => { - info.serviceName = '@some$service' - }).toThrow('serviceName "@some$service" is invalid. Only allowed to have a-z, A-Z, 0-9, -, _ as characters.') - }) + it('throws on invalid service name', () => { + const info = new Proxy( + { + serviceName: '', + serviceDescription: '', + serviceVersion: '1', + }, + ServiceInfoValidator, + ) + expect(() => { + info.serviceName = '@some$service' + }).toThrow('serviceName "@some$service" is invalid. Only allowed to have a-z, A-Z, 0-9, -, _ as characters.') + }) - it('throws on empty service version', () => { - const info = new Proxy( - { - serviceName: '', - serviceDescription: '', - serviceVersion: '1', - }, - ServiceInfoValidator, - ) - expect(() => { - info.serviceVersion = '' - }).toThrow('serviceVersion must be set') - }) + it('throws on empty service version', () => { + const info = new Proxy( + { + serviceName: '', + serviceDescription: '', + serviceVersion: '1', + }, + ServiceInfoValidator, + ) + expect(() => { + info.serviceVersion = '' + }).toThrow('serviceVersion must be set') + }) - it('throws on invalid service version', () => { - const info = new Proxy( - { - serviceName: '', - serviceDescription: '', - serviceVersion: '1', - }, - ServiceInfoValidator, - ) - expect(() => { - info.serviceVersion = 'x' - }).toThrow('serviceVersion "x" is invalid. Only allowed number characters 0-9.') - }) + it('throws on invalid service version', () => { + const info = new Proxy( + { + serviceName: '', + serviceDescription: '', + serviceVersion: '1', + }, + ServiceInfoValidator, + ) + expect(() => { + info.serviceVersion = 'x' + }).toThrow('serviceVersion "x" is invalid. Only allowed number characters 0-9.') + }) }) diff --git a/packages/core/src/core/Service/subscriptionTransformInput.impl.ts b/packages/core/src/core/Service/subscriptionTransformInput.impl.ts index 389eba1c5..6addf7efb 100644 --- a/packages/core/src/core/Service/subscriptionTransformInput.impl.ts +++ b/packages/core/src/core/Service/subscriptionTransformInput.impl.ts @@ -3,104 +3,105 @@ import { validate } from '@typeschema/main' import { HandledError, UnhandledError } from '../Error/index.js' import type { EBMessage, Logger, SubscriptionDefinition } from '../types/index.js' -import { isCommand, StatusCode } from '../types/index.js' +import { StatusCode, isCommand } from '../types/index.js' import type { Service } from './Service.impl.js' -export const subscriptionTransformInput = async ( - serviceInstance: Service, - logger: Logger, - subscription: SubscriptionDefinition, - message: Readonly, +export const subscriptionTransformInput = async ( + serviceInstance: S, + logger: Logger, + subscription: SubscriptionDefinition, + message: Readonly, ) => { - let msgPayload: { payload: unknown; parameter: unknown } + let msgPayload: { payload: unknown; parameter: unknown } - if (isCommand(message)) { - msgPayload = message.payload - } else { - msgPayload = { payload: message.payload, parameter: undefined } - } + if (isCommand(message)) { + msgPayload = message.payload + } else { + msgPayload = { payload: message.payload, parameter: undefined } + } - if (!subscription.hooks.transformInput) { - return msgPayload as Readonly<{ payload: Readonly; parameter: Readonly }> - } + if (!subscription.hooks.transformInput) { + return msgPayload as Readonly<{ payload: Readonly; parameter: Readonly }> + } - const transformInput = subscription.hooks.transformInput - return await serviceInstance.startActiveSpan( - subscription.subscriptionName + '.inputTransformation', - {}, - undefined, - async (_) => { - const transform = transformInput.transformFunction.bind(serviceInstance, { - message, - ...serviceInstance.getContextFunctions(logger), - }) - const parameterInput = await serviceInstance.wrapInSpan( - subscription.subscriptionName + '.validateParameter', - {}, - async (subSpan) => { - const validationResult = await validate(transformInput.transformParameterSchema, msgPayload?.parameter) - if (validationResult.success) { - return validationResult.data as Readonly - } - const err = new HandledError(StatusCode.BadRequest, undefined, validationResult.issues) - subSpan.recordException(err) - logger.warn( - { ...subSpan.spanContext(), err }, - 'transform input validation for parameters failed:', - err.message, - ) + const transformInput = subscription.hooks.transformInput + return await serviceInstance.startActiveSpan( + `${subscription.subscriptionName}.inputTransformation`, + {}, + undefined, + async _ => { + const transform = transformInput.transformFunction.bind(serviceInstance, { + message, + ...serviceInstance.getContextFunctions(logger), + resources: serviceInstance.resources, + }) + const parameterInput = await serviceInstance.wrapInSpan( + `${subscription.subscriptionName}.validateParameter`, + {}, + async subSpan => { + const validationResult = await validate(transformInput.transformParameterSchema, msgPayload?.parameter) + if (validationResult.success) { + return validationResult.data as Readonly + } + const err = new HandledError(StatusCode.BadRequest, undefined, validationResult.issues) + subSpan.recordException(err) + logger.warn( + { ...subSpan.spanContext(), err }, + 'transform input validation for parameters failed:', + err.message, + ) - subSpan.setStatus({ - code: SpanStatusCode.ERROR, - message: 'transform input validation for parameters failed', - }) - throw err - }, - ) + subSpan.setStatus({ + code: SpanStatusCode.ERROR, + message: 'transform input validation for parameters failed', + }) + throw err + }, + ) - const payloadInput = await serviceInstance.wrapInSpan( - subscription.subscriptionName + '.validatePayload', - {}, - async (subSpan) => { - const validationResult = await validate(transformInput.transformInputSchema, msgPayload?.payload) - if (validationResult.success) { - return validationResult.data as Readonly - } - const err = new HandledError(StatusCode.BadRequest, undefined, validationResult.issues) - subSpan.recordException(err) - logger.warn({ ...subSpan.spanContext(), err }, 'transform input validation for payload failed:', err.message) + const payloadInput = await serviceInstance.wrapInSpan( + `${subscription.subscriptionName}.validatePayload`, + {}, + async subSpan => { + const validationResult = await validate(transformInput.transformInputSchema, msgPayload?.payload) + if (validationResult.success) { + return validationResult.data as Readonly + } + const err = new HandledError(StatusCode.BadRequest, undefined, validationResult.issues) + subSpan.recordException(err) + logger.warn({ ...subSpan.spanContext(), err }, 'transform input validation for payload failed:', err.message) - subSpan.setStatus({ - code: SpanStatusCode.ERROR, - message: 'transform input validation for payload failed', - }) - throw err - }, - ) + subSpan.setStatus({ + code: SpanStatusCode.ERROR, + message: 'transform input validation for payload failed', + }) + throw err + }, + ) - return await serviceInstance.wrapInSpan( - subscription.subscriptionName + '.transformFunction', - {}, - async (subSpan) => { - try { - return await transform(payloadInput, parameterInput) - } catch (error) { - const err = error as Error - subSpan.recordException(err) - subSpan.setStatus({ - code: SpanStatusCode.ERROR, - message: err.message || 'Unable to transform input', - }) + return await serviceInstance.wrapInSpan( + `${subscription.subscriptionName}.transformFunction`, + {}, + async subSpan => { + try { + return await transform(payloadInput, parameterInput) + } catch (error) { + const err = error as Error + subSpan.recordException(err) + subSpan.setStatus({ + code: SpanStatusCode.ERROR, + message: err.message || 'Unable to transform input', + }) - if (error instanceof HandledError) { - throw error - } - logger.error({ err, ...subSpan.spanContext() }, 'Unable to transform input:') + if (error instanceof HandledError) { + throw error + } + logger.error({ err, ...subSpan.spanContext() }, 'Unable to transform input:') - throw new UnhandledError(StatusCode.InternalServerError, 'Unable to transform input') - } - }, - ) - }, - ) + throw new UnhandledError(StatusCode.InternalServerError, 'Unable to transform input') + } + }, + ) + }, + ) } diff --git a/packages/core/src/core/Service/subscriptionTransformInput.test.ts b/packages/core/src/core/Service/subscriptionTransformInput.test.ts index 9a9b2b08e..c237f96ba 100644 --- a/packages/core/src/core/Service/subscriptionTransformInput.test.ts +++ b/packages/core/src/core/Service/subscriptionTransformInput.test.ts @@ -1,5 +1,5 @@ describe('subscriptionTransformInput', () => { - it.todo('throws HandledError if parameter schema validation fails') - it.todo('throws HandledError if payload schema validation fails') - it.todo('throws HandledError if output schema validation fails') + it.todo('throws HandledError if parameter schema validation fails') + it.todo('throws HandledError if payload schema validation fails') + it.todo('throws HandledError if output schema validation fails') }) diff --git a/packages/core/src/core/StateStore/StateStoreBaseClass.impl.ts b/packages/core/src/core/StateStore/StateStoreBaseClass.impl.ts index a27d07641..caab5e14d 100644 --- a/packages/core/src/core/StateStore/StateStoreBaseClass.impl.ts +++ b/packages/core/src/core/StateStore/StateStoreBaseClass.impl.ts @@ -1,7 +1,7 @@ import { initLogger } from '../../DefaultLogger/index.js' import type { ObjectWithKeysFromStringArray } from '../../helper/index.js' import { UnhandledError } from '../Error/index.js' -import type { Logger, StoreBaseConfig } from '../types/index.js' +import type { EmptyObject, Logger, StoreBaseConfig } from '../types/index.js' import { StatusCode } from '../types/index.js' /** @@ -15,71 +15,71 @@ import { StatusCode } from '../types/index.js' * __DO NOT OVERWRITE__: the regular methods getState, setState or removeState * @group Store */ -export abstract class StateStoreBaseClass = {}> { - logger: Logger - config: StoreBaseConfig +export abstract class StateStoreBaseClass = EmptyObject> { + logger: Logger + config: StoreBaseConfig - name: string + name: string - constructor(name: string, config: StoreBaseConfig) { - const logger = config?.logger ?? initLogger(config?.logLevel) - this.logger = logger.getChildLogger({ name }) + constructor(name: string, config: StoreBaseConfig) { + const logger = config?.logger ?? initLogger(config?.logLevel) + this.logger = logger.getChildLogger({ name }) - this.name = name + this.name = name - this.config = { - enableGet: true, - enableSet: true, - enableRemove: true, - ...config, - } - } + this.config = { + enableGet: true, + enableSet: true, + enableRemove: true, + ...config, + } + } - protected abstract getStateImpl( - // eslint-disable-next-line @typescript-eslint/no-unused-vars - ...stateNames: StateNames - ): Promise> + protected abstract getStateImpl( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + ...stateNames: StateNames + ): Promise> - async getState( - ...stateNames: StateNames - ): Promise> { - if (!this.config.enableGet) { - const err = new UnhandledError(StatusCode.Unauthorized, 'get state from store is disabled by config') - this.logger.error({ err }, err.message) - throw err - } + async getState( + ...stateNames: StateNames + ): Promise> { + if (!this.config.enableGet) { + const err = new UnhandledError(StatusCode.Unauthorized, 'get state from store is disabled by config') + this.logger.error({ err }, err.message) + throw err + } - return this.getStateImpl(...stateNames) - } + return this.getStateImpl(...stateNames) + } - // eslint-disable-next-line @typescript-eslint/no-unused-vars - protected abstract removeStateImpl(stateName: string): Promise + // eslint-disable-next-line @typescript-eslint/no-unused-vars + protected abstract removeStateImpl(stateName: string): Promise - async removeState(stateName: string) { - if (!this.config.enableRemove) { - const err = new UnhandledError(StatusCode.Unauthorized, 'remove state from store is disabled by config') - this.logger.error({ err }, err.message) - throw err - } + async removeState(stateName: string) { + if (!this.config.enableRemove) { + const err = new UnhandledError(StatusCode.Unauthorized, 'remove state from store is disabled by config') + this.logger.error({ err }, err.message) + throw err + } - return this.removeStateImpl(stateName) - } + return this.removeStateImpl(stateName) + } - // eslint-disable-next-line @typescript-eslint/no-unused-vars - protected abstract setStateImpl(stateName: string, stateValue: unknown): Promise + // eslint-disable-next-line @typescript-eslint/no-unused-vars + protected abstract setStateImpl(stateName: string, stateValue: unknown): Promise - // eslint-disable-next-line @typescript-eslint/no-unused-vars - async setState(stateName: string, stateValue: unknown) { - if (!this.config.enableSet) { - const err = new UnhandledError(StatusCode.Unauthorized, 'set state at store is disabled by config') - this.logger.error({ err }, err.message) - throw err - } + // eslint-disable-next-line @typescript-eslint/no-unused-vars + async setState(stateName: string, stateValue: unknown) { + if (!this.config.enableSet) { + const err = new UnhandledError(StatusCode.Unauthorized, 'set state at store is disabled by config') + this.logger.error({ err }, err.message) + throw err + } - return this.setStateImpl(stateName, stateValue) - } + return this.setStateImpl(stateName, stateValue) + } - async destroy() { - this.logger.info('stopped') - } + async destroy() { + this.logger.info('stopped') + } } diff --git a/packages/core/src/core/StateStore/stateStoreBaseClass.test.ts b/packages/core/src/core/StateStore/stateStoreBaseClass.test.ts index 757f91648..c6b4f75f2 100644 --- a/packages/core/src/core/StateStore/stateStoreBaseClass.test.ts +++ b/packages/core/src/core/StateStore/stateStoreBaseClass.test.ts @@ -8,88 +8,88 @@ import { StatusCode } from '../types/StatusCode.enum.js' import { StateStoreBaseClass } from './StateStoreBaseClass.impl.js' class TestClass extends StateStoreBaseClass { - protected getStateImpl( - ..._stateNames: StateNames - ): Promise> { - throw new Error('Not implemented') - } - - protected setStateImpl(_stateName: string, _stateValue: unknown): Promise { - throw new Error('Not implemented') - } - - protected removeStateImpl(_stateName: string): Promise { - throw new Error('Not implemented') - } + protected getStateImpl( + ..._stateNames: StateNames + ): Promise> { + throw new Error('Not implemented') + } + + protected setStateImpl(_stateName: string, _stateValue: unknown): Promise { + throw new Error('Not implemented') + } + + protected removeStateImpl(_stateName: string): Promise { + throw new Error('Not implemented') + } } describe('StateStoreBaseClass', () => { - let sandbox: SinonSandbox - let stateStore: StateStoreBaseClass - let logger: ReturnType + let sandbox: SinonSandbox + let stateStore: StateStoreBaseClass + let logger: ReturnType - beforeEach(() => { - sandbox = createSandbox() - logger = getLoggerMock(sandbox) - stateStore = new TestClass('test', { logger: logger.mock }) - }) + beforeEach(() => { + sandbox = createSandbox() + logger = getLoggerMock(sandbox) + stateStore = new TestClass('test', { logger: logger.mock }) + }) - afterEach(() => { - sandbox.restore() - }) + afterEach(() => { + sandbox.restore() + }) - describe('getState', () => { - it('should throw an UnhandledError if enableGet is false', async () => { - sandbox.stub(stateStore.config, 'enableGet').value(false) + describe('getState', () => { + it('should throw an UnhandledError if enableGet is false', async () => { + sandbox.stub(stateStore.config, 'enableGet').value(false) - await expect(stateStore.getState('test')).rejects.toEqual( - new UnhandledError(StatusCode.Unauthorized, 'get state from store is disabled by config'), - ) + await expect(stateStore.getState('test')).rejects.toEqual( + new UnhandledError(StatusCode.Unauthorized, 'get state from store is disabled by config'), + ) - sandbox.assert.calledOnce(logger.stubs.error) - }) + sandbox.assert.calledOnce(logger.stubs.error) + }) - it('should throw an UnhandledError if enableGet is true but method is not implemented', async () => { - sandbox.stub(stateStore.config, 'enableGet').value(true) + it('should throw an UnhandledError if enableGet is true but method is not implemented', async () => { + sandbox.stub(stateStore.config, 'enableGet').value(true) - await expect(stateStore.getState('test')).rejects.toEqual(new Error('Not implemented')) - }) - }) + await expect(stateStore.getState('test')).rejects.toEqual(new Error('Not implemented')) + }) + }) - describe('setState', () => { - it('should throw an UnhandledError if enableSet is false', async () => { - sandbox.stub(stateStore.config, 'enableSet').value(false) + describe('setState', () => { + it('should throw an UnhandledError if enableSet is false', async () => { + sandbox.stub(stateStore.config, 'enableSet').value(false) - await expect(stateStore.setState('test', 'state_value')).rejects.toEqual( - new UnhandledError(StatusCode.Unauthorized, 'set state at store is disabled by config'), - ) + await expect(stateStore.setState('test', 'state_value')).rejects.toEqual( + new UnhandledError(StatusCode.Unauthorized, 'set state at store is disabled by config'), + ) - sandbox.assert.calledOnce(logger.stubs.error) - }) + sandbox.assert.calledOnce(logger.stubs.error) + }) - it('should throw an UnhandledError if enableSet is true but method is not implemented', async () => { - // Arrange - sandbox.stub(stateStore.config, 'enableSet').value(true) + it('should throw an UnhandledError if enableSet is true but method is not implemented', async () => { + // Arrange + sandbox.stub(stateStore.config, 'enableSet').value(true) - await expect(stateStore.setState('test', 'state_value')).rejects.toEqual(new Error('Not implemented')) - }) - }) + await expect(stateStore.setState('test', 'state_value')).rejects.toEqual(new Error('Not implemented')) + }) + }) - describe('removeState', () => { - it('should throw an UnhandledError if enableRemove is false', async () => { - sandbox.stub(stateStore.config, 'enableRemove').value(false) + describe('removeState', () => { + it('should throw an UnhandledError if enableRemove is false', async () => { + sandbox.stub(stateStore.config, 'enableRemove').value(false) - await expect(stateStore.removeState('test')).rejects.toMatchObject( - new UnhandledError(StatusCode.Unauthorized, 'remove state from store is disabled by config'), - ) + await expect(stateStore.removeState('test')).rejects.toMatchObject( + new UnhandledError(StatusCode.Unauthorized, 'remove state from store is disabled by config'), + ) - sandbox.assert.calledOnce(logger.stubs.error) - }) + sandbox.assert.calledOnce(logger.stubs.error) + }) - it('should throw an UnhandledError if enableRemove is true but method is not implemented', async () => { - sandbox.stub(stateStore.config, 'enableRemove').value(true) + it('should throw an UnhandledError if enableRemove is true but method is not implemented', async () => { + sandbox.stub(stateStore.config, 'enableRemove').value(true) - await expect(stateStore.removeState('test')).rejects.toMatchObject(new Error('Not implemented')) - }) - }) + await expect(stateStore.removeState('test')).rejects.toMatchObject(new Error('Not implemented')) + }) + }) }) diff --git a/packages/core/src/core/StateStore/types/StateGetterFunction.ts b/packages/core/src/core/StateStore/types/StateGetterFunction.ts index d7359469b..defc37501 100644 --- a/packages/core/src/core/StateStore/types/StateGetterFunction.ts +++ b/packages/core/src/core/StateStore/types/StateGetterFunction.ts @@ -2,5 +2,5 @@ import type { ObjectWithKeysFromStringArray } from '../../../helper/types/Object /** get a state value from the state store @group Store */ export type StateGetterFunction = ( - ...stateNames: StateNames + ...stateNames: StateNames ) => Promise> diff --git a/packages/core/src/core/StateStore/types/StateStore.ts b/packages/core/src/core/StateStore/types/StateStore.ts index 0b8c9a8d5..b2ed7d130 100644 --- a/packages/core/src/core/StateStore/types/StateStore.ts +++ b/packages/core/src/core/StateStore/types/StateStore.ts @@ -8,33 +8,33 @@ import type { StateSetterFunction } from './StateSetterFunction.js' * @group Store */ export interface StateStore { - /** name of store */ - name: string - /** - * get a state value - * @param string name of state - * @returns the state - * @throws UnhandledError - */ - getState: StateGetterFunction + /** name of store */ + name: string + /** + * get a state value + * @param string name of state + * @returns the state + * @throws UnhandledError + */ + getState: StateGetterFunction - /** - * delete a state value - * @param string name of state - * @throws UnhandledError - */ - removeState: StateDeleteFunction + /** + * delete a state value + * @param string name of state + * @throws UnhandledError + */ + removeState: StateDeleteFunction - /** - * set a state value - * @param string name of state - * @param value value of state - * @throws UnhandledError - */ - setState: StateSetterFunction + /** + * set a state value + * @param string name of state + * @param value value of state + * @throws UnhandledError + */ + setState: StateSetterFunction - /** - * disconnects and shuts down the state store - */ - destroy(): Promise + /** + * disconnects and shuts down the state store + */ + destroy(): Promise } diff --git a/packages/core/src/core/helper/createErrorResponse.impl.ts b/packages/core/src/core/helper/createErrorResponse.impl.ts index a2db28a0f..72a768ec4 100644 --- a/packages/core/src/core/helper/createErrorResponse.impl.ts +++ b/packages/core/src/core/helper/createErrorResponse.impl.ts @@ -17,50 +17,50 @@ import { serializeOtp } from './serializeOtp.impl.js' * @group Helper */ export const createErrorResponse = ( - instanceId: InstanceId, - originalEBMessage: Readonly, - statusCode = StatusCode.InternalServerError, - error?: unknown | string | Error | HandledError | UnhandledError, + instanceId: InstanceId, + originalEBMessage: Readonly, + statusCode = StatusCode.InternalServerError, + error?: unknown | string | Error | HandledError | UnhandledError, ): Readonly> => { - const message = getErrorMessageForCode(statusCode) - const status = statusCode - const isHandledError = error instanceof HandledError + const message = getErrorMessageForCode(statusCode) + const status = statusCode + const isHandledError = error instanceof HandledError - let errorTraceId: TraceId | undefined - if (error instanceof HandledError || error instanceof UnhandledError) { - errorTraceId = error.traceId - } + let errorTraceId: TraceId | undefined + if (error instanceof HandledError || error instanceof UnhandledError) { + errorTraceId = error.traceId + } - const traceId = originalEBMessage.traceId ?? errorTraceId ?? getNewTraceId() + const traceId = originalEBMessage.traceId ?? errorTraceId ?? getNewTraceId() - const errorResponse: Readonly = Object.freeze({ - id: originalEBMessage.id, - isHandledError, - traceId, - principalId: originalEBMessage.principalId, - tenantId: originalEBMessage.tenantId, - contentType: 'application/json', - contentEncoding: 'utf-8', - correlationId: originalEBMessage.correlationId, - timestamp: Date.now(), - messageType: EBMessageType.CommandErrorResponse, - sender: { - ...originalEBMessage.receiver, - instanceId, - }, - receiver: { - ...originalEBMessage.sender, - }, - payload: - error instanceof HandledError - ? error.getErrorResponse(traceId) - : { - status, - message, - traceId, - }, - otp: serializeOtp(), - }) + const errorResponse: Readonly = Object.freeze({ + id: originalEBMessage.id, + isHandledError, + traceId, + principalId: originalEBMessage.principalId, + tenantId: originalEBMessage.tenantId, + contentType: 'application/json', + contentEncoding: 'utf-8', + correlationId: originalEBMessage.correlationId, + timestamp: Date.now(), + messageType: EBMessageType.CommandErrorResponse, + sender: { + ...originalEBMessage.receiver, + instanceId, + }, + receiver: { + ...originalEBMessage.sender, + }, + payload: + error instanceof HandledError + ? error.getErrorResponse(traceId) + : { + status, + message, + traceId, + }, + otp: serializeOtp(), + }) - return errorResponse + return errorResponse } diff --git a/packages/core/src/core/helper/createErrorResponse.test.ts b/packages/core/src/core/helper/createErrorResponse.test.ts index b1693904f..558a73464 100644 --- a/packages/core/src/core/helper/createErrorResponse.test.ts +++ b/packages/core/src/core/helper/createErrorResponse.test.ts @@ -4,93 +4,93 @@ import { EBMessageType, StatusCode } from '../types/index.js' import { createErrorResponse } from './createErrorResponse.impl.js' describe('createErrorResponse', () => { - const message: Command = { - messageType: EBMessageType.Command, - id: 'messageTestId', - traceId: 'testTraceId', - timestamp: Date.now(), - contentType: 'application/json', - contentEncoding: 'utf-8', - correlationId: 'messageCorrelationId', - principalId: 'messagePrincipalId', - tenantId: 'messageTenantId', - sender: { - serviceName: 'SenderService', - serviceVersion: '1.1.1', - serviceTarget: 'senderServiceTarget', - instanceId: 'SenderServiceInstance', - }, - - receiver: { - serviceName: 'ReceiverService', - serviceVersion: '2.2.2', - serviceTarget: 'receiverServiceTarget', - instanceId: 'ReceiverServiceInstance', - }, - payload: { - payload: { input: 'input payload' }, - parameter: { input: 'parameter' }, - }, - } - - const statusCode = StatusCode.InternalServerError - - it('creates a error response', () => { - const result = createErrorResponse('ReceiverServiceInstance', message, statusCode) - - const payload = { status: statusCode, message: 'Internal Server Error', traceId: message.traceId } - - expect(result.messageType).toBe(EBMessageType.CommandErrorResponse) - expect(result.payload).toStrictEqual(payload) - expect(result.sender).toStrictEqual(message.receiver) - expect(result.receiver).toStrictEqual(message.sender) - expect(result.traceId).toBe(message.traceId) - expect(result.correlationId).toBe(message.correlationId) - expect(result.id).toBe(message.id) - expect(result.tenantId).toBe(message.tenantId) - expect(result.principalId).toBe(message.principalId) - expect(result.isHandledError).toBeFalsy() - }) - - it('creates a error response with HandledError', () => { - const data = { some: 'additional data' } - - const messageText = 'invalid input - some fields missing' - - const error = new HandledError(StatusCode.BadRequest, messageText, data) - - const result = createErrorResponse('ReceiverServiceInstance', message, statusCode, error) - - const payload = { status: StatusCode.BadRequest, message: messageText, traceId: message.traceId, data } - - expect(result.messageType).toBe(EBMessageType.CommandErrorResponse) - expect(result.payload).toStrictEqual(payload) - expect(result.sender).toStrictEqual(message.receiver) - expect(result.receiver).toStrictEqual(message.sender) - expect(result.traceId).toBe(message.traceId) - expect(result.correlationId).toBe(message.correlationId) - expect(result.id).toBe(message.id) - expect(result.isHandledError).toBeTruthy() - }) - - it('creates a error response with UnhandledError', () => { - const data = { some: 'additional data' } - - const messageText = 'invalid input - some fields missing' - - const error = new UnhandledError(StatusCode.BadRequest, messageText, data) - - const result = createErrorResponse('ReceiverServiceInstance', message, statusCode, error) - - const payload = { status: statusCode, message: 'Internal Server Error', traceId: message.traceId } - - expect(result.messageType).toBe(EBMessageType.CommandErrorResponse) - expect(result.payload).toStrictEqual(payload) - expect(result.sender).toStrictEqual(message.receiver) - expect(result.receiver).toStrictEqual(message.sender) - expect(result.traceId).toBe(message.traceId) - expect(result.correlationId).toBe(message.correlationId) - expect(result.id).toBe(message.id) - expect(result.isHandledError).toBeFalsy() - }) + const message: Command = { + messageType: EBMessageType.Command, + id: 'messageTestId', + traceId: 'testTraceId', + timestamp: Date.now(), + contentType: 'application/json', + contentEncoding: 'utf-8', + correlationId: 'messageCorrelationId', + principalId: 'messagePrincipalId', + tenantId: 'messageTenantId', + sender: { + serviceName: 'SenderService', + serviceVersion: '1.1.1', + serviceTarget: 'senderServiceTarget', + instanceId: 'SenderServiceInstance', + }, + + receiver: { + serviceName: 'ReceiverService', + serviceVersion: '2.2.2', + serviceTarget: 'receiverServiceTarget', + instanceId: 'ReceiverServiceInstance', + }, + payload: { + payload: { input: 'input payload' }, + parameter: { input: 'parameter' }, + }, + } + + const statusCode = StatusCode.InternalServerError + + it('creates a error response', () => { + const result = createErrorResponse('ReceiverServiceInstance', message, statusCode) + + const payload = { status: statusCode, message: 'Internal Server Error', traceId: message.traceId } + + expect(result.messageType).toBe(EBMessageType.CommandErrorResponse) + expect(result.payload).toStrictEqual(payload) + expect(result.sender).toStrictEqual(message.receiver) + expect(result.receiver).toStrictEqual(message.sender) + expect(result.traceId).toBe(message.traceId) + expect(result.correlationId).toBe(message.correlationId) + expect(result.id).toBe(message.id) + expect(result.tenantId).toBe(message.tenantId) + expect(result.principalId).toBe(message.principalId) + expect(result.isHandledError).toBeFalsy() + }) + + it('creates a error response with HandledError', () => { + const data = { some: 'additional data' } + + const messageText = 'invalid input - some fields missing' + + const error = new HandledError(StatusCode.BadRequest, messageText, data) + + const result = createErrorResponse('ReceiverServiceInstance', message, statusCode, error) + + const payload = { status: StatusCode.BadRequest, message: messageText, traceId: message.traceId, data } + + expect(result.messageType).toBe(EBMessageType.CommandErrorResponse) + expect(result.payload).toStrictEqual(payload) + expect(result.sender).toStrictEqual(message.receiver) + expect(result.receiver).toStrictEqual(message.sender) + expect(result.traceId).toBe(message.traceId) + expect(result.correlationId).toBe(message.correlationId) + expect(result.id).toBe(message.id) + expect(result.isHandledError).toBeTruthy() + }) + + it('creates a error response with UnhandledError', () => { + const data = { some: 'additional data' } + + const messageText = 'invalid input - some fields missing' + + const error = new UnhandledError(StatusCode.BadRequest, messageText, data) + + const result = createErrorResponse('ReceiverServiceInstance', message, statusCode, error) + + const payload = { status: statusCode, message: 'Internal Server Error', traceId: message.traceId } + + expect(result.messageType).toBe(EBMessageType.CommandErrorResponse) + expect(result.payload).toStrictEqual(payload) + expect(result.sender).toStrictEqual(message.receiver) + expect(result.receiver).toStrictEqual(message.sender) + expect(result.traceId).toBe(message.traceId) + expect(result.correlationId).toBe(message.correlationId) + expect(result.id).toBe(message.id) + expect(result.isHandledError).toBeFalsy() + }) }) diff --git a/packages/core/src/core/helper/createInfoMessage.impl.ts b/packages/core/src/core/helper/createInfoMessage.impl.ts index 6846658a8..0f68ae1dc 100644 --- a/packages/core/src/core/helper/createInfoMessage.impl.ts +++ b/packages/core/src/core/helper/createInfoMessage.impl.ts @@ -12,20 +12,20 @@ import { getNewTraceId } from './getNewTraceId.impl.js' * @group Helper */ export const createInfoMessage = ( - messageType: InfoMessageType, - sender: EBMessageSenderAddress, - additional?: Partial, + messageType: InfoMessageType, + sender: EBMessageSenderAddress, + additional?: Partial, ): InfoMessage => { - const info: Readonly = Object.freeze({ - messageType, - id: getNewEBMessageId(), - traceId: getNewTraceId(), - timestamp: Date.now(), - contentType: 'application/json', - contentEncoding: 'utf-8', - sender, - ...additional, - }) + const info: Readonly = Object.freeze({ + messageType, + id: getNewEBMessageId(), + traceId: getNewTraceId(), + timestamp: Date.now(), + contentType: 'application/json', + contentEncoding: 'utf-8', + sender, + ...additional, + }) - return info + return info } diff --git a/packages/core/src/core/helper/createInfoMessage.test.ts b/packages/core/src/core/helper/createInfoMessage.test.ts index bd1f6049e..982c19a42 100644 --- a/packages/core/src/core/helper/createInfoMessage.test.ts +++ b/packages/core/src/core/helper/createInfoMessage.test.ts @@ -2,22 +2,22 @@ import { EBMessageType } from '../types/index.js' import { createInfoMessage } from './createInfoMessage.impl.js' describe('createInfoMessage', () => { - it('creates a info message', () => { - const payload = { content: 'result content' } + it('creates a info message', () => { + const payload = { content: 'result content' } - const sender = { - serviceName: 'SenderService', - serviceVersion: '1.1.1', - serviceTarget: 'senderServiceTarget', - instanceId: 'a', - } + const sender = { + serviceName: 'SenderService', + serviceVersion: '1.1.1', + serviceTarget: 'senderServiceTarget', + instanceId: 'a', + } - const result = createInfoMessage(EBMessageType.InfoServiceInit, sender, { payload }) + const result = createInfoMessage(EBMessageType.InfoServiceInit, sender, { payload }) - expect(result.messageType).toBe(EBMessageType.InfoServiceInit) - expect(result.sender).toStrictEqual(sender) - expect(result.traceId).toBeDefined() - expect(result.id).toBeDefined() - expect(result.timestamp).toBeDefined() - }) + expect(result.messageType).toBe(EBMessageType.InfoServiceInit) + expect(result.sender).toStrictEqual(sender) + expect(result.traceId).toBeDefined() + expect(result.id).toBeDefined() + expect(result.timestamp).toBeDefined() + }) }) diff --git a/packages/core/src/core/helper/createInvokeFunctionProxy.impl.ts b/packages/core/src/core/helper/createInvokeFunctionProxy.impl.ts index a8e15a1c5..af291e076 100644 --- a/packages/core/src/core/helper/createInvokeFunctionProxy.impl.ts +++ b/packages/core/src/core/helper/createInvokeFunctionProxy.impl.ts @@ -2,7 +2,7 @@ import type { EBMessageAddress } from '../types/EBMessageAddress.js' import type { InvokeFunction } from '../types/index.js' const noop = () => { - // noop + // noop } /** @@ -14,54 +14,54 @@ const noop = () => { * @returns a proxy which allows to chain like serviceName.serviceVersion.fnToInvoke(payload,parameter) */ export const createInvokeFunctionProxy = ( - invokeOg: InvokeFunction, - address?: EBMessageAddress, - lvl = 0, + invokeOg: InvokeFunction, + address?: EBMessageAddress, + lvl = 0, ): TFaux => { - const adr = { - serviceName: '', - serviceTarget: '', - serviceVersion: '', - ...address, - } + const adr = { + serviceName: '', + serviceTarget: '', + serviceVersion: '', + ...address, + } - return new Proxy(noop, { - get(obj: Record, name) { - if (typeof name !== 'string' || name === 'then' || name === 'catch' || name === 'finally') { - // special case for if the proxy is accidentally treated - // like a PromiseLike (like in `Promise.resolve(proxy)`) - return undefined - } + return new Proxy(noop, { + get(obj: Record, name) { + if (typeof name !== 'string' || name === 'then' || name === 'catch' || name === 'finally') { + // special case for if the proxy is accidentally treated + // like a PromiseLike (like in `Promise.resolve(proxy)`) + return undefined + } - const x = obj[name] - if (lvl === 0) { - const na = { - ...adr, - serviceName: name, - } - return createInvokeFunctionProxy(invokeOg, na, lvl + 1) - } - if (lvl === 1) { - const na = { - ...adr, - serviceVersion: name, - } - return createInvokeFunctionProxy(invokeOg, na, lvl + 1) - } + const x = obj[name] + if (lvl === 0) { + const na = { + ...adr, + serviceName: name, + } + return createInvokeFunctionProxy(invokeOg, na, lvl + 1) + } + if (lvl === 1) { + const na = { + ...adr, + serviceVersion: name, + } + return createInvokeFunctionProxy(invokeOg, na, lvl + 1) + } - if (lvl === 2) { - const na = { - ...adr, - serviceTarget: name, - } - return (payload: Parameters[0], parameter: Parameters[1]) => { - return invokeOg[0], Parameters[1], ReturnType>( - na, - payload, - parameter, - ) - } - } - }, - }) as TFaux + if (lvl === 2) { + const na = { + ...adr, + serviceTarget: name, + } + return (payload: Parameters[0], parameter: Parameters[1]) => { + return invokeOg[0], Parameters[1], ReturnType>( + na, + payload, + parameter, + ) + } + } + }, + }) as TFaux } diff --git a/packages/core/src/core/helper/createSuccessResponse.impl.ts b/packages/core/src/core/helper/createSuccessResponse.impl.ts index 8443ef4ba..6896d8a6f 100644 --- a/packages/core/src/core/helper/createSuccessResponse.impl.ts +++ b/packages/core/src/core/helper/createSuccessResponse.impl.ts @@ -14,33 +14,33 @@ import { getNewTraceId } from './getNewTraceId.impl.js' * @group Helper */ export const createSuccessResponse = ( - instanceId: InstanceId, - originalEBMessage: Readonly, - payload: T, - eventName?: string, - contentType = 'application/json', - contentEncoding = 'utf-8', + instanceId: InstanceId, + originalEBMessage: Readonly, + payload: T, + eventName?: string, + contentType = 'application/json', + contentEncoding = 'utf-8', ): Readonly> => { - const successResponse: CommandSuccessResponse = Object.freeze({ - id: originalEBMessage.id, - correlationId: originalEBMessage.correlationId, - traceId: originalEBMessage.traceId ?? getNewTraceId(), - principalId: originalEBMessage.principalId, - tenantId: originalEBMessage.tenantId, - contentType, - contentEncoding, - timestamp: Date.now(), - eventName, - messageType: EBMessageType.CommandSuccessResponse, - sender: { - ...originalEBMessage.receiver, - instanceId, - }, - receiver: { - ...originalEBMessage.sender, - }, - payload, - }) + const successResponse: CommandSuccessResponse = Object.freeze({ + id: originalEBMessage.id, + correlationId: originalEBMessage.correlationId, + traceId: originalEBMessage.traceId ?? getNewTraceId(), + principalId: originalEBMessage.principalId, + tenantId: originalEBMessage.tenantId, + contentType, + contentEncoding, + timestamp: Date.now(), + eventName, + messageType: EBMessageType.CommandSuccessResponse, + sender: { + ...originalEBMessage.receiver, + instanceId, + }, + receiver: { + ...originalEBMessage.sender, + }, + payload, + }) - return successResponse + return successResponse } diff --git a/packages/core/src/core/helper/createSuccessResponse.test.ts b/packages/core/src/core/helper/createSuccessResponse.test.ts index e9009ea2f..56b448e7b 100644 --- a/packages/core/src/core/helper/createSuccessResponse.test.ts +++ b/packages/core/src/core/helper/createSuccessResponse.test.ts @@ -3,86 +3,86 @@ import { EBMessageType } from '../types/index.js' import { createSuccessResponse } from './createSuccessResponse.impl.js' describe('createSuccessResponse', () => { - it('creates a success response', () => { - const payload = { content: 'result content' } + it('creates a success response', () => { + const payload = { content: 'result content' } - const message: Command = { - messageType: EBMessageType.Command, - id: 'messageTestId', - traceId: 'testTraceId', - timestamp: Date.now(), - correlationId: 'messageCorrelationId', - principalId: 'messagePrincipalId', - tenantId: 'messageTenantId', - sender: { - serviceName: 'SenderService', - serviceVersion: '1.1.1', - serviceTarget: 'senderServiceTarget', - instanceId: 'SenderServiceInstance', - }, - contentType: 'application/json', - contentEncoding: 'utf-8', - receiver: { - serviceName: 'ReceiverService', - serviceVersion: '2.2.2', - serviceTarget: 'receiverServiceTarget', - instanceId: 'ReceiverServiceInstance', - }, - payload: { - payload: { input: 'input payload' }, - parameter: { input: 'parameter' }, - }, - } + const message: Command = { + messageType: EBMessageType.Command, + id: 'messageTestId', + traceId: 'testTraceId', + timestamp: Date.now(), + correlationId: 'messageCorrelationId', + principalId: 'messagePrincipalId', + tenantId: 'messageTenantId', + sender: { + serviceName: 'SenderService', + serviceVersion: '1.1.1', + serviceTarget: 'senderServiceTarget', + instanceId: 'SenderServiceInstance', + }, + contentType: 'application/json', + contentEncoding: 'utf-8', + receiver: { + serviceName: 'ReceiverService', + serviceVersion: '2.2.2', + serviceTarget: 'receiverServiceTarget', + instanceId: 'ReceiverServiceInstance', + }, + payload: { + payload: { input: 'input payload' }, + parameter: { input: 'parameter' }, + }, + } - const result = createSuccessResponse('ReceiverServiceInstance', message, payload) + const result = createSuccessResponse('ReceiverServiceInstance', message, payload) - expect(result.messageType).toBe(EBMessageType.CommandSuccessResponse) - expect(result.payload).toBe(payload) - expect(result.sender).toStrictEqual(message.receiver) - expect(result.receiver).toStrictEqual(message.sender) - expect(result.traceId).toBe(message.traceId) - expect(result.correlationId).toBe(message.correlationId) - expect(result.tenantId).toBe(message.tenantId) - expect(result.principalId).toBe(message.principalId) - expect(result.id).toBe(message.id) - }) + expect(result.messageType).toBe(EBMessageType.CommandSuccessResponse) + expect(result.payload).toBe(payload) + expect(result.sender).toStrictEqual(message.receiver) + expect(result.receiver).toStrictEqual(message.sender) + expect(result.traceId).toBe(message.traceId) + expect(result.correlationId).toBe(message.correlationId) + expect(result.tenantId).toBe(message.tenantId) + expect(result.principalId).toBe(message.principalId) + expect(result.id).toBe(message.id) + }) - it('adds a trace id', () => { - const payload = { content: 'result content' } + it('adds a trace id', () => { + const payload = { content: 'result content' } - const message: Command = { - messageType: EBMessageType.Command, - id: 'messageTestId', - timestamp: Date.now(), - correlationId: 'messageCorrelationId', - principalId: 'messagePrincipalId', - tenantId: 'messageTenantId', - sender: { - serviceName: 'SenderService', - serviceVersion: '1.1.1', - serviceTarget: 'senderServiceTarget', - instanceId: 'SenderServiceInstance', - }, - contentType: 'application/json', - contentEncoding: 'utf-8', - receiver: { - serviceName: 'ReceiverService', - serviceVersion: '2.2.2', - serviceTarget: 'receiverServiceTarget', - instanceId: 'ReceiverServiceInstance', - }, - payload: { - payload: { input: 'input payload' }, - parameter: { input: 'parameter' }, - }, - } + const message: Command = { + messageType: EBMessageType.Command, + id: 'messageTestId', + timestamp: Date.now(), + correlationId: 'messageCorrelationId', + principalId: 'messagePrincipalId', + tenantId: 'messageTenantId', + sender: { + serviceName: 'SenderService', + serviceVersion: '1.1.1', + serviceTarget: 'senderServiceTarget', + instanceId: 'SenderServiceInstance', + }, + contentType: 'application/json', + contentEncoding: 'utf-8', + receiver: { + serviceName: 'ReceiverService', + serviceVersion: '2.2.2', + serviceTarget: 'receiverServiceTarget', + instanceId: 'ReceiverServiceInstance', + }, + payload: { + payload: { input: 'input payload' }, + parameter: { input: 'parameter' }, + }, + } - const result = createSuccessResponse('ReceiverServiceInstance', message, payload) + const result = createSuccessResponse('ReceiverServiceInstance', message, payload) - expect(result.messageType).toBe(EBMessageType.CommandSuccessResponse) - expect(result.payload).toBe(payload) - expect(result.sender).toStrictEqual(message.receiver) - expect(result.receiver).toStrictEqual(message.sender) - expect(result.traceId).toBeDefined() - }) + expect(result.messageType).toBe(EBMessageType.CommandSuccessResponse) + expect(result.payload).toBe(payload) + expect(result.sender).toStrictEqual(message.receiver) + expect(result.receiver).toStrictEqual(message.sender) + expect(result.traceId).toBeDefined() + }) }) diff --git a/packages/core/src/core/helper/getCleanedMessage.impl.ts b/packages/core/src/core/helper/getCleanedMessage.impl.ts index 8d168eddf..1138b07bd 100644 --- a/packages/core/src/core/helper/getCleanedMessage.impl.ts +++ b/packages/core/src/core/helper/getCleanedMessage.impl.ts @@ -14,31 +14,31 @@ import { isDevelop } from './isDevelop.impl.js' * @group Helper */ export const getCleanedMessage = ( - message: Readonly, - stripeOutContent = !isDevelop(), + message: Readonly, + stripeOutContent = !isDevelop(), ): Record => { - // return full message if stripeOutContent is set to false - if (!stripeOutContent) { - return message - } + // return full message if stripeOutContent is set to false + if (!stripeOutContent) { + return message + } - const cleanedMessage: EBMessage = { - ...message, - } + const cleanedMessage: EBMessage = { + ...message, + } - if (isCommand(cleanedMessage)) { - cleanedMessage.payload = { - ...cleanedMessage.payload, - parameter: { - all: '***removed from log***', - }, - payload: '***removed from log***', - } - } + if (isCommand(cleanedMessage)) { + cleanedMessage.payload = { + ...cleanedMessage.payload, + parameter: { + all: '***removed from log***', + }, + payload: '***removed from log***', + } + } - if (isCommandSuccessResponse(cleanedMessage)) { - cleanedMessage.payload = '***removed from log***' - } + if (isCommandSuccessResponse(cleanedMessage)) { + cleanedMessage.payload = '***removed from log***' + } - return cleanedMessage + return cleanedMessage } diff --git a/packages/core/src/core/helper/getCleanedMessage.test.ts b/packages/core/src/core/helper/getCleanedMessage.test.ts index c2ce6a2e9..c9fd528af 100644 --- a/packages/core/src/core/helper/getCleanedMessage.test.ts +++ b/packages/core/src/core/helper/getCleanedMessage.test.ts @@ -3,108 +3,108 @@ import { EBMessageType } from '../types/index.js' import { getCleanedMessage } from './getCleanedMessage.impl.js' describe('prevent sensitive data to be logged in production', () => { - it('returns the original message', () => { - const message: Command = { - sender: { - serviceName: 'SenderService', - serviceVersion: '1.1.1', - serviceTarget: 'senderServiceTarget', - instanceId: 'SenderServiceInstance', - }, + it('returns the original message', () => { + const message: Command = { + sender: { + serviceName: 'SenderService', + serviceVersion: '1.1.1', + serviceTarget: 'senderServiceTarget', + instanceId: 'SenderServiceInstance', + }, - receiver: { - serviceName: 'ReceiverService', - serviceVersion: '2.2.2', - serviceTarget: 'receiverServiceTarget', - instanceId: 'ReceiverServiceInstance', - }, - contentType: 'application/json', - contentEncoding: 'utf-8', - messageType: EBMessageType.Command, - id: 'messageTestId', - traceId: 'messageTraceId', - timestamp: Date.now(), - correlationId: 'messageCorrelationId', - principalId: 'messagePrincipalId', - tenantId: 'messageTenantId', - payload: { - payload: { content: 'content' }, - parameter: { param: 1 }, - }, - } + receiver: { + serviceName: 'ReceiverService', + serviceVersion: '2.2.2', + serviceTarget: 'receiverServiceTarget', + instanceId: 'ReceiverServiceInstance', + }, + contentType: 'application/json', + contentEncoding: 'utf-8', + messageType: EBMessageType.Command, + id: 'messageTestId', + traceId: 'messageTraceId', + timestamp: Date.now(), + correlationId: 'messageCorrelationId', + principalId: 'messagePrincipalId', + tenantId: 'messageTenantId', + payload: { + payload: { content: 'content' }, + parameter: { param: 1 }, + }, + } - const result = getCleanedMessage(message, false) as Command + const result = getCleanedMessage(message, false) as Command - expect(result).toBe(message) - }) + expect(result).toBe(message) + }) - it('removes payload and parameter from command message', () => { - const message: Command = { - sender: { - serviceName: 'SenderService', - serviceVersion: '1.1.1', - serviceTarget: 'senderServiceTarget', - instanceId: 'SenderServiceInstance', - }, + it('removes payload and parameter from command message', () => { + const message: Command = { + sender: { + serviceName: 'SenderService', + serviceVersion: '1.1.1', + serviceTarget: 'senderServiceTarget', + instanceId: 'SenderServiceInstance', + }, - receiver: { - serviceName: 'ReceiverService', - serviceVersion: '2.2.2', - serviceTarget: 'receiverServiceTarget', - instanceId: 'ReceiverServiceInstance', - }, - contentType: 'application/json', - contentEncoding: 'utf-8', - messageType: EBMessageType.Command, - id: 'messageTestId', - traceId: 'messageTraceId', - timestamp: Date.now(), - correlationId: 'messageCorrelationId', - principalId: 'messagePrincipalId', - tenantId: 'messageTenantId', - payload: { - payload: { content: 'content' }, - parameter: { param: 1 }, - }, - } + receiver: { + serviceName: 'ReceiverService', + serviceVersion: '2.2.2', + serviceTarget: 'receiverServiceTarget', + instanceId: 'ReceiverServiceInstance', + }, + contentType: 'application/json', + contentEncoding: 'utf-8', + messageType: EBMessageType.Command, + id: 'messageTestId', + traceId: 'messageTraceId', + timestamp: Date.now(), + correlationId: 'messageCorrelationId', + principalId: 'messagePrincipalId', + tenantId: 'messageTenantId', + payload: { + payload: { content: 'content' }, + parameter: { param: 1 }, + }, + } - const result = getCleanedMessage(message, true) as Command + const result = getCleanedMessage(message, true) as Command - expect(result).not.toStrictEqual(message) - expect(result.payload.payload).toBe('***removed from log***') - expect(result.payload.parameter.all).toBe('***removed from log***') - }) + expect(result).not.toStrictEqual(message) + expect(result.payload.payload).toBe('***removed from log***') + expect(result.payload.parameter.all).toBe('***removed from log***') + }) - it('removes response from command success message', () => { - const message: CommandSuccessResponse = { - sender: { - serviceName: 'SenderService', - serviceVersion: '1.1.1', - serviceTarget: 'senderServiceTarget', - instanceId: 'SenderServiceInstance', - }, + it('removes response from command success message', () => { + const message: CommandSuccessResponse = { + sender: { + serviceName: 'SenderService', + serviceVersion: '1.1.1', + serviceTarget: 'senderServiceTarget', + instanceId: 'SenderServiceInstance', + }, - receiver: { - serviceName: 'ReceiverService', - serviceVersion: '2.2.2', - serviceTarget: 'receiverServiceTarget', - instanceId: 'ReceiverServiceInstance', - }, - contentType: 'application/json', - contentEncoding: 'utf-8', - messageType: EBMessageType.CommandSuccessResponse, - id: 'messageTestId', - traceId: 'messageTraceId', - timestamp: Date.now(), - correlationId: 'messageCorrelationId', - principalId: 'messagePrincipalId', - tenantId: 'messageTenantId', - payload: { content: 'content' }, - } + receiver: { + serviceName: 'ReceiverService', + serviceVersion: '2.2.2', + serviceTarget: 'receiverServiceTarget', + instanceId: 'ReceiverServiceInstance', + }, + contentType: 'application/json', + contentEncoding: 'utf-8', + messageType: EBMessageType.CommandSuccessResponse, + id: 'messageTestId', + traceId: 'messageTraceId', + timestamp: Date.now(), + correlationId: 'messageCorrelationId', + principalId: 'messagePrincipalId', + tenantId: 'messageTenantId', + payload: { content: 'content' }, + } - const result = getCleanedMessage(message, true) as CommandSuccessResponse + const result = getCleanedMessage(message, true) as CommandSuccessResponse - expect(result).not.toStrictEqual(message) - expect(result.payload).toBe('***removed from log***') - }) + expect(result).not.toStrictEqual(message) + expect(result.payload).toBe('***removed from log***') + }) }) diff --git a/packages/core/src/core/helper/getCommandQueueName.impl.ts b/packages/core/src/core/helper/getCommandQueueName.impl.ts index 0048df7f7..42bad8fe5 100644 --- a/packages/core/src/core/helper/getCommandQueueName.impl.ts +++ b/packages/core/src/core/helper/getCommandQueueName.impl.ts @@ -8,5 +8,5 @@ import type { EBMessageAddress } from '../types/index.js' * @group Event bridge */ export const getCommandQueueName = (address: EBMessageAddress): string => { - return `cq-${address.serviceName}-${address.serviceVersion}-${address.serviceTarget}`.toLocaleLowerCase() + return `cq-${address.serviceName}-${address.serviceVersion}-${address.serviceTarget}`.toLocaleLowerCase() } diff --git a/packages/core/src/core/helper/getCommandQueueName.test.ts b/packages/core/src/core/helper/getCommandQueueName.test.ts index 9abf8241b..2027c30cd 100644 --- a/packages/core/src/core/helper/getCommandQueueName.test.ts +++ b/packages/core/src/core/helper/getCommandQueueName.test.ts @@ -2,15 +2,15 @@ import type { EBMessageAddress } from '../types/index.js' import { getCommandQueueName } from './getCommandQueueName.impl.js' describe('creates a command function queue name', () => { - it('returns a id string', () => { - const address: EBMessageAddress = { - serviceName: 'serviceName', - serviceVersion: '1', - serviceTarget: 'myFunction', - } + it('returns a id string', () => { + const address: EBMessageAddress = { + serviceName: 'serviceName', + serviceVersion: '1', + serviceTarget: 'myFunction', + } - const id = getCommandQueueName(address) + const id = getCommandQueueName(address) - expect(id).toBe('cq-servicename-1-myfunction') - }) + expect(id).toBe('cq-servicename-1-myfunction') + }) }) diff --git a/packages/core/src/core/helper/getErrorMessageForCode.impl.ts b/packages/core/src/core/helper/getErrorMessageForCode.impl.ts index 94e6a6d99..60e2a5a7d 100644 --- a/packages/core/src/core/helper/getErrorMessageForCode.impl.ts +++ b/packages/core/src/core/helper/getErrorMessageForCode.impl.ts @@ -8,18 +8,18 @@ import { StatusCode } from '../types/index.js' * @group Helper */ export const getErrorMessageForCode = (code: StatusCode): string => { - const entry = StatusCode[code] + const entry = StatusCode[code] - if (!entry) { - return 'Unknown Error' - } + if (!entry) { + return 'Unknown Error' + } - const capitalizeFirstLetter = (input: string) => { - return input.charAt(0).toUpperCase() + input.slice(1) - } + const capitalizeFirstLetter = (input: string) => { + return input.charAt(0).toUpperCase() + input.slice(1) + } - const transform = (input: string) => - input.replace(/[A-Z]+(?![a-z])|[A-Z]/g, ($, ofs) => (ofs ? ' ' : '') + capitalizeFirstLetter($)) + const transform = (input: string) => + input.replace(/[A-Z]+(?![a-z])|[A-Z]/g, ($, ofs) => (ofs ? ' ' : '') + capitalizeFirstLetter($)) - return transform(StatusCode[code]) + return transform(StatusCode[code]) } diff --git a/packages/core/src/core/helper/getErrorMessageForCode.test.ts b/packages/core/src/core/helper/getErrorMessageForCode.test.ts index 9375d5dfe..cf821d850 100644 --- a/packages/core/src/core/helper/getErrorMessageForCode.test.ts +++ b/packages/core/src/core/helper/getErrorMessageForCode.test.ts @@ -2,9 +2,9 @@ import type { StatusCode } from '../types/index.js' import { getErrorMessageForCode } from './getErrorMessageForCode.impl.js' it('returns a message string for error code', () => { - expect(getErrorMessageForCode(200)).toBe('OK') + expect(getErrorMessageForCode(200)).toBe('OK') - expect(getErrorMessageForCode(400)).toBe('Bad Request') + expect(getErrorMessageForCode(400)).toBe('Bad Request') - expect(getErrorMessageForCode(900 as StatusCode)).toBe('Unknown Error') + expect(getErrorMessageForCode(900 as StatusCode)).toBe('Unknown Error') }) diff --git a/packages/core/src/core/helper/getNewCorrelationId.test.ts b/packages/core/src/core/helper/getNewCorrelationId.test.ts index 8bdf7a80b..986d0f542 100644 --- a/packages/core/src/core/helper/getNewCorrelationId.test.ts +++ b/packages/core/src/core/helper/getNewCorrelationId.test.ts @@ -1,17 +1,17 @@ import { getNewCorrelationId } from './getNewCorrelationId.impl.js' it('returns a unique id', () => { - const id1 = getNewCorrelationId() - expect(id1).toBeDefined() - expect(id1).not.toBeNaN() - expect(id1).not.toBeNull() - expect(typeof id1).toBe('string') + const id1 = getNewCorrelationId() + expect(id1).toBeDefined() + expect(id1).not.toBeNaN() + expect(id1).not.toBeNull() + expect(typeof id1).toBe('string') - const id2 = getNewCorrelationId() - expect(id2).toBeDefined() - expect(id2).not.toBeNaN() - expect(id2).not.toBeNull() - expect(typeof id2).toBe('string') + const id2 = getNewCorrelationId() + expect(id2).toBeDefined() + expect(id2).not.toBeNaN() + expect(id2).not.toBeNull() + expect(typeof id2).toBe('string') - expect(id1).not.toBe(id2) + expect(id1).not.toBe(id2) }) diff --git a/packages/core/src/core/helper/getNewEBMessageId.test.ts b/packages/core/src/core/helper/getNewEBMessageId.test.ts index 7d6ecceff..de02a4ad3 100644 --- a/packages/core/src/core/helper/getNewEBMessageId.test.ts +++ b/packages/core/src/core/helper/getNewEBMessageId.test.ts @@ -1,17 +1,17 @@ import { getNewEBMessageId } from './getNewEBMessageId.impl.js' it('returns a unique id', () => { - const id1 = getNewEBMessageId() - expect(id1).toBeDefined() - expect(id1).not.toBeNaN() - expect(id1).not.toBeNull() - expect(typeof id1).toBe('string') + const id1 = getNewEBMessageId() + expect(id1).toBeDefined() + expect(id1).not.toBeNaN() + expect(id1).not.toBeNull() + expect(typeof id1).toBe('string') - const id2 = getNewEBMessageId() - expect(id2).toBeDefined() - expect(id2).not.toBeNaN() - expect(id2).not.toBeNull() - expect(typeof id2).toBe('string') + const id2 = getNewEBMessageId() + expect(id2).toBeDefined() + expect(id2).not.toBeNaN() + expect(id2).not.toBeNull() + expect(typeof id2).toBe('string') - expect(id1).not.toBe(id2) + expect(id1).not.toBe(id2) }) diff --git a/packages/core/src/core/helper/getNewInstanceId.test.ts b/packages/core/src/core/helper/getNewInstanceId.test.ts index 32486cc0f..f8a01b469 100644 --- a/packages/core/src/core/helper/getNewInstanceId.test.ts +++ b/packages/core/src/core/helper/getNewInstanceId.test.ts @@ -1,17 +1,17 @@ import { getNewInstanceId } from './getNewInstanceId.impl.js' it('returns a unique id', () => { - const id1 = getNewInstanceId() - expect(id1).toBeDefined() - expect(id1).not.toBeNaN() - expect(id1).not.toBeNull() - expect(typeof id1).toBe('string') + const id1 = getNewInstanceId() + expect(id1).toBeDefined() + expect(id1).not.toBeNaN() + expect(id1).not.toBeNull() + expect(typeof id1).toBe('string') - const id2 = getNewInstanceId() - expect(id2).toBeDefined() - expect(id2).not.toBeNaN() - expect(id2).not.toBeNull() - expect(typeof id2).toBe('string') + const id2 = getNewInstanceId() + expect(id2).toBeDefined() + expect(id2).not.toBeNaN() + expect(id2).not.toBeNull() + expect(typeof id2).toBe('string') - expect(id1).not.toBe(id2) + expect(id1).not.toBe(id2) }) diff --git a/packages/core/src/core/helper/getNewTraceId.test.ts b/packages/core/src/core/helper/getNewTraceId.test.ts index c446429d0..4175414b8 100644 --- a/packages/core/src/core/helper/getNewTraceId.test.ts +++ b/packages/core/src/core/helper/getNewTraceId.test.ts @@ -1,17 +1,17 @@ import { getNewTraceId } from './getNewTraceId.impl.js' it('returns a unique id', () => { - const id1 = getNewTraceId() - expect(id1).toBeDefined() - expect(id1).not.toBeNaN() - expect(id1).not.toBeNull() - expect(typeof id1).toBe('string') + const id1 = getNewTraceId() + expect(id1).toBeDefined() + expect(id1).not.toBeNaN() + expect(id1).not.toBeNull() + expect(typeof id1).toBe('string') - const id2 = getNewTraceId() - expect(id2).toBeDefined() - expect(id2).not.toBeNaN() - expect(id2).not.toBeNull() - expect(typeof id2).toBe('string') + const id2 = getNewTraceId() + expect(id2).toBeDefined() + expect(id2).not.toBeNaN() + expect(id2).not.toBeNull() + expect(typeof id2).toBe('string') - expect(id1).not.toBe(id2) + expect(id1).not.toBe(id2) }) diff --git a/packages/core/src/core/helper/getSubscriptionQueueName.impl.ts b/packages/core/src/core/helper/getSubscriptionQueueName.impl.ts index 23c8754b3..2992cef08 100644 --- a/packages/core/src/core/helper/getSubscriptionQueueName.impl.ts +++ b/packages/core/src/core/helper/getSubscriptionQueueName.impl.ts @@ -8,5 +8,5 @@ import type { EBMessageAddress } from '../types/index.js' * @group Helper */ export const getSubscriptionQueueName = (address: EBMessageAddress): string => { - return `sq-${address.serviceName}-${address.serviceVersion}-${address.serviceTarget}`.toLocaleLowerCase() + return `sq-${address.serviceName}-${address.serviceVersion}-${address.serviceTarget}`.toLocaleLowerCase() } diff --git a/packages/core/src/core/helper/getSubscriptionQueueName.test.ts b/packages/core/src/core/helper/getSubscriptionQueueName.test.ts index 206c18190..000353fd3 100644 --- a/packages/core/src/core/helper/getSubscriptionQueueName.test.ts +++ b/packages/core/src/core/helper/getSubscriptionQueueName.test.ts @@ -2,15 +2,15 @@ import type { EBMessageAddress } from '../types/index.js' import { getSubscriptionQueueName } from './getSubscriptionQueueName.impl.js' describe('creates a subscription queue name', () => { - it('returns a id string', () => { - const address: EBMessageAddress = { - serviceName: 'serviceName', - serviceVersion: '1', - serviceTarget: 'mySubscription', - } + it('returns a id string', () => { + const address: EBMessageAddress = { + serviceName: 'serviceName', + serviceVersion: '1', + serviceTarget: 'mySubscription', + } - const id = getSubscriptionQueueName(address) + const id = getSubscriptionQueueName(address) - expect(id).toBe('sq-servicename-1-mysubscription') - }) + expect(id).toBe('sq-servicename-1-mysubscription') + }) }) diff --git a/packages/core/src/core/helper/getUniqueId.test.ts b/packages/core/src/core/helper/getUniqueId.test.ts index 336bc53d2..778c6d9b8 100644 --- a/packages/core/src/core/helper/getUniqueId.test.ts +++ b/packages/core/src/core/helper/getUniqueId.test.ts @@ -1,17 +1,17 @@ import { getUniqueId } from './getUniqueId.impl.js' it('returns a unique id', () => { - const id1 = getUniqueId() - expect(id1).toBeDefined() - expect(id1).not.toBeNaN() - expect(id1).not.toBeNull() - expect(typeof id1).toBe('string') + const id1 = getUniqueId() + expect(id1).toBeDefined() + expect(id1).not.toBeNaN() + expect(id1).not.toBeNull() + expect(typeof id1).toBe('string') - const id2 = getUniqueId() - expect(id2).toBeDefined() - expect(id2).not.toBeNaN() - expect(id2).not.toBeNull() - expect(typeof id2).toBe('string') + const id2 = getUniqueId() + expect(id2).toBeDefined() + expect(id2).not.toBeNaN() + expect(id2).not.toBeNull() + expect(typeof id2).toBe('string') - expect(id1).not.toBe(id2) + expect(id1).not.toBe(id2) }) diff --git a/packages/core/src/core/helper/isDevelop.impl.ts b/packages/core/src/core/helper/isDevelop.impl.ts index 67920f69f..0b79a091c 100644 --- a/packages/core/src/core/helper/isDevelop.impl.ts +++ b/packages/core/src/core/helper/isDevelop.impl.ts @@ -4,7 +4,7 @@ * @group Helper */ export const isDevelop = (): boolean => { - const nodeEnv = process.env.NODE_ENV + const nodeEnv = process.env.NODE_ENV - return nodeEnv ? nodeEnv.toLowerCase().startsWith('develop') : false + return nodeEnv ? nodeEnv.toLowerCase().startsWith('develop') : false } diff --git a/packages/core/src/core/helper/isDevelop.test.ts b/packages/core/src/core/helper/isDevelop.test.ts index 985fd8127..34a2af04c 100644 --- a/packages/core/src/core/helper/isDevelop.test.ts +++ b/packages/core/src/core/helper/isDevelop.test.ts @@ -1,38 +1,38 @@ import { isDevelop } from './isDevelop.impl.js' describe('check if env is develop or production', () => { - const env = process.env - - beforeEach(() => { - process.env = { ...env } - }) - - afterEach(() => { - process.env = env - }) - - it('returns true if NODE_ENV is develop', () => { - process.env.NODE_ENV = 'develop' as any - expect(isDevelop()).toBeTruthy() - }) - - it('returns true if NODE_ENV is development', () => { - process.env.NODE_ENV = 'development' - expect(isDevelop()).toBeTruthy() - }) - - it('returns false if NODE_ENV is production', () => { - process.env.NODE_ENV = 'production' - expect(isDevelop()).toBeFalsy() - }) - - it('returns false if NODE_ENV different from develop', () => { - process.env.NODE_ENV = 'something' as any - expect(isDevelop()).toBeFalsy() - }) - - it('returns false if NODE_ENV is not set', () => { - process.env.NODE_ENV = undefined as any - expect(isDevelop()).toBeFalsy() - }) + const env = process.env + + beforeEach(() => { + process.env = { ...env } + }) + + afterEach(() => { + process.env = env + }) + + it('returns true if NODE_ENV is develop', () => { + process.env.NODE_ENV = 'develop' as any + expect(isDevelop()).toBeTruthy() + }) + + it('returns true if NODE_ENV is development', () => { + process.env.NODE_ENV = 'development' + expect(isDevelop()).toBeTruthy() + }) + + it('returns false if NODE_ENV is production', () => { + process.env.NODE_ENV = 'production' + expect(isDevelop()).toBeFalsy() + }) + + it('returns false if NODE_ENV different from develop', () => { + process.env.NODE_ENV = 'something' as any + expect(isDevelop()).toBeFalsy() + }) + + it('returns false if NODE_ENV is not set', () => { + process.env.NODE_ENV = undefined as any + expect(isDevelop()).toBeFalsy() + }) }) diff --git a/packages/core/src/core/helper/serializeOtp.impl.ts b/packages/core/src/core/helper/serializeOtp.impl.ts index a33ec5ef8..9831f4e2a 100644 --- a/packages/core/src/core/helper/serializeOtp.impl.ts +++ b/packages/core/src/core/helper/serializeOtp.impl.ts @@ -9,10 +9,10 @@ import type { Logger } from '../types/index.js' * @group Helper */ export const serializeOtp = () => { - const serializedContext = {} + const serializedContext = {} - propagation.inject(context.active(), serializedContext) - return JSON.stringify(serializedContext) + propagation.inject(context.active(), serializedContext) + return JSON.stringify(serializedContext) } /** @@ -24,15 +24,15 @@ export const serializeOtp = () => { * @group Helper */ export const deserializeOtp = (logger: Logger, otp?: string) => { - try { - let header = {} + try { + let header = {} - if (otp) { - header = JSON.parse(otp) - } + if (otp) { + header = JSON.parse(otp) + } - return propagation.extract(context.active(), header) - } catch (err) { - logger.error({ err }, 'unable to deserialize otp entry in message') - } + return propagation.extract(context.active(), header) + } catch (err) { + logger.error({ err }, 'unable to deserialize otp entry in message') + } } diff --git a/packages/core/src/core/helper/serializeOtp.test.ts b/packages/core/src/core/helper/serializeOtp.test.ts index 26d6e8196..457683673 100644 --- a/packages/core/src/core/helper/serializeOtp.test.ts +++ b/packages/core/src/core/helper/serializeOtp.test.ts @@ -2,16 +2,16 @@ import { getLoggerMock } from '../../mocks/index.js' import { deserializeOtp, serializeOtp } from './serializeOtp.impl.js' describe('serializeOtp', () => { - it('serializes opentelemetry', () => { - const result = serializeOtp() - expect(result).toBeDefined() - }) + it('serializes opentelemetry', () => { + const result = serializeOtp() + expect(result).toBeDefined() + }) - it('logs error and returns undefined JSON.parse fails', async () => { - const logger = getLoggerMock() - const result = deserializeOtp(logger.mock, 'wrong_input') + it('logs error and returns undefined JSON.parse fails', async () => { + const logger = getLoggerMock() + const result = deserializeOtp(logger.mock, 'wrong_input') - expect(result).toBeUndefined() - expect(logger.stubs.error.called).toBeTruthy() - }) + expect(result).toBeUndefined() + expect(logger.stubs.error.called).toBeTruthy() + }) }) diff --git a/packages/core/src/core/types/BrokerHeaderCommandMsg.ts b/packages/core/src/core/types/BrokerHeaderCommandMsg.ts index 57a3f841a..f98ab9101 100644 --- a/packages/core/src/core/types/BrokerHeaderCommandMsg.ts +++ b/packages/core/src/core/types/BrokerHeaderCommandMsg.ts @@ -3,10 +3,10 @@ import type { InstanceId } from './InstanceId.js' import type { Prettify } from './Prettify.js' export type BrokerHeaderCommandMsg = Prettify< - BrokerHeaderCustomMsg & { - receiverServiceName: string - receiverServiceVersion: string - receiverServiceTarget: string - receiverInstanceId?: InstanceId - } + BrokerHeaderCustomMsg & { + receiverServiceName: string + receiverServiceVersion: string + receiverServiceTarget: string + receiverInstanceId?: InstanceId + } > diff --git a/packages/core/src/core/types/BrokerHeaderCommandResponseMsg.ts b/packages/core/src/core/types/BrokerHeaderCommandResponseMsg.ts index 2cff5ad7b..2f40f2a9c 100644 --- a/packages/core/src/core/types/BrokerHeaderCommandResponseMsg.ts +++ b/packages/core/src/core/types/BrokerHeaderCommandResponseMsg.ts @@ -3,7 +3,7 @@ import type { InstanceId } from './InstanceId.js' import type { Prettify } from './Prettify.js' export type BrokerHeaderCommandResponseMsg = Prettify< - BrokerHeaderCommandMsg & { - receiverInstanceId: InstanceId - } + BrokerHeaderCommandMsg & { + receiverInstanceId: InstanceId + } > diff --git a/packages/core/src/core/types/BrokerHeaderCustomMsg.ts b/packages/core/src/core/types/BrokerHeaderCustomMsg.ts index 0606e2724..4d874c703 100644 --- a/packages/core/src/core/types/BrokerHeaderCustomMsg.ts +++ b/packages/core/src/core/types/BrokerHeaderCustomMsg.ts @@ -4,12 +4,12 @@ import type { PrincipalId } from './PrincipalId.js' import type { TenantId } from './TenantId.js' export type BrokerHeaderCustomMsg = { - messageType: EBMessageType - senderServiceName: string - senderServiceVersion: string - senderServiceTarget: string - senderInstanceId: InstanceId - eventName?: string - principalId?: PrincipalId - tenantId?: TenantId + messageType: EBMessageType + senderServiceName: string + senderServiceVersion: string + senderServiceTarget: string + senderInstanceId: InstanceId + eventName?: string + principalId?: PrincipalId + tenantId?: TenantId } diff --git a/packages/core/src/core/types/Complete.ts b/packages/core/src/core/types/Complete.ts index c2ce86f45..29859643e 100644 --- a/packages/core/src/core/types/Complete.ts +++ b/packages/core/src/core/types/Complete.ts @@ -19,5 +19,5 @@ * ``` */ export type Complete = { - [P in keyof Required]: Pick extends Required> ? T[P] : T[P] | undefined + [P in keyof Required]: Pick extends Required> ? T[P] : T[P] | undefined } diff --git a/packages/core/src/core/types/ContentType.ts b/packages/core/src/core/types/ContentType.ts index ea09321cd..0fb09ee9a 100644 --- a/packages/core/src/core/types/ContentType.ts +++ b/packages/core/src/core/types/ContentType.ts @@ -4,13 +4,13 @@ * It is up to the implementation to extract the content type from the original message and to convert or transform data. */ export type ContentType = - | 'application/json' - | 'application/javascript' - | 'text/csv' - | 'text/css' - | 'text/html' - | 'text/javascript' - | 'text/markdown' - | 'text/plain' - | 'text/xml' - | string + | 'application/json' + | 'application/javascript' + | 'text/csv' + | 'text/css' + | 'text/html' + | 'text/javascript' + | 'text/markdown' + | 'text/plain' + | 'text/xml' + | string diff --git a/packages/core/src/core/types/ContextBase.ts b/packages/core/src/core/types/ContextBase.ts index b16140f95..eb44bef2a 100644 --- a/packages/core/src/core/types/ContextBase.ts +++ b/packages/core/src/core/types/ContextBase.ts @@ -10,42 +10,42 @@ import type { Logger } from './Logger.js' * Each context for command function, subscription function and all Hooks and transformers will have at least the properties of this type. */ export type ContextBase = { - /** the logger instance */ - logger: Logger - /** wrap given function in an opentelemetry span */ - wrapInSpan: (name: string, opts: SpanOptions, fn: (span: Span) => Promise, context?: Context) => Promise - /** wrap given function in an opentelemetry active span */ - startActiveSpan: ( - name: string, - opts: SpanOptions, - context: Context | undefined, - fn: (span: Span) => Promise, - ) => Promise - /** the secret store */ - secrets: { - /** get a secret from the secret store */ - getSecret: SecretGetterFunction - /** set a secret in the secret store */ - setSecret: SecretSetterFunction - /** delete a secret from the secret store */ - removeSecret: SecretDeleteFunction - } - /** the config store */ - configs: { - /** get a config value from the config store */ - getConfig: ConfigGetterFunction - /** set a config value in the config store */ - setConfig: ConfigSetterFunction - /** delete a config value from the config store */ - removeConfig: ConfigDeleteFunction - } - /** the state store */ - states: { - /** get a state value from the state store */ - getState: StateGetterFunction - /** set a state value in the state store */ - setState: StateSetterFunction - /** delete a state value from the state store */ - removeState: StateDeleteFunction - } + /** the logger instance */ + logger: Logger + /** wrap given function in an opentelemetry span */ + wrapInSpan: (name: string, opts: SpanOptions, fn: (span: Span) => Promise, context?: Context) => Promise + /** wrap given function in an opentelemetry active span */ + startActiveSpan: ( + name: string, + opts: SpanOptions, + context: Context | undefined, + fn: (span: Span) => Promise, + ) => Promise + /** the secret store */ + secrets: { + /** get a secret from the secret store */ + getSecret: SecretGetterFunction + /** set a secret in the secret store */ + setSecret: SecretSetterFunction + /** delete a secret from the secret store */ + removeSecret: SecretDeleteFunction + } + /** the config store */ + configs: { + /** get a config value from the config store */ + getConfig: ConfigGetterFunction + /** set a config value in the config store */ + setConfig: ConfigSetterFunction + /** delete a config value from the config store */ + removeConfig: ConfigDeleteFunction + } + /** the state store */ + states: { + /** get a state value from the state store */ + getState: StateGetterFunction + /** set a state value in the state store */ + setState: StateSetterFunction + /** delete a state value from the state store */ + removeState: StateDeleteFunction + } } diff --git a/packages/core/src/core/types/CustomMessage.ts b/packages/core/src/core/types/CustomMessage.ts index 08ee0c7ec..098dd87e3 100644 --- a/packages/core/src/core/types/CustomMessage.ts +++ b/packages/core/src/core/types/CustomMessage.ts @@ -9,14 +9,14 @@ import type { Prettify } from './Prettify.js' * The producer does not expect a response from a consumer. */ export type CustomMessage = Prettify< - { - /** Message type musst be EBMessageType.CustomMessage */ - messageType: EBMessageType.CustomMessage - /** the event name assigned to this custom message */ - eventName: string - /** an optional receiver */ - receiver?: EBMessageAddress - /** the message payload */ - payload?: Payload - } & EBMessageBase + { + /** Message type musst be EBMessageType.CustomMessage */ + messageType: EBMessageType.CustomMessage + /** the event name assigned to this custom message */ + eventName: string + /** an optional receiver */ + receiver?: EBMessageAddress + /** the message payload */ + payload?: Payload + } & EBMessageBase > diff --git a/packages/core/src/core/types/DefinitionEventBridgeConfig.ts b/packages/core/src/core/types/DefinitionEventBridgeConfig.ts index 376b910a1..47bfc6111 100644 --- a/packages/core/src/core/types/DefinitionEventBridgeConfig.ts +++ b/packages/core/src/core/types/DefinitionEventBridgeConfig.ts @@ -4,28 +4,28 @@ * It depends on the used event bridge implementation and underlaying message broker, if a specific property can be respected. */ export type DefinitionEventBridgeConfig = { - /** - * Advise the underlaying message broker to store messages if no consumer is available. - * Messages will be send as soon as the service is able to consume. - * */ - durable: boolean - /** - * Send the acknowledge to message broker as soon as the message arrives - * - defaults to true for commands - * - defaults to false for subscriptions - * - * */ - autoacknowledge: boolean - /** - * If set to true, the event bridge is adviced to deliver one message to at least one consumer instance. - * True is the default value. - * If set to false, the event bridge is adviced to deliver one message to all consumer instances. - * - * Use case: Receiving Info of message, which need to be passed to all instance to keep information in sync. - * - * In serverless environments, this flag should not have any effect - * - * @default true - */ - shared: boolean + /** + * Advise the underlaying message broker to store messages if no consumer is available. + * Messages will be send as soon as the service is able to consume. + * */ + durable: boolean + /** + * Send the acknowledge to message broker as soon as the message arrives + * - defaults to true for commands + * - defaults to false for subscriptions + * + * */ + autoacknowledge: boolean + /** + * If set to true, the event bridge is adviced to deliver one message to at least one consumer instance. + * True is the default value. + * If set to false, the event bridge is adviced to deliver one message to all consumer instances. + * + * Use case: Receiving Info of message, which need to be passed to all instance to keep information in sync. + * + * In serverless environments, this flag should not have any effect + * + * @default true + */ + shared: boolean } diff --git a/packages/core/src/core/types/EBMessage.ts b/packages/core/src/core/types/EBMessage.ts index f163f150b..9e687f897 100644 --- a/packages/core/src/core/types/EBMessage.ts +++ b/packages/core/src/core/types/EBMessage.ts @@ -1,5 +1,5 @@ -import type { Command, CommandResponse } from './commandType/index.js' import type { CustomMessage } from './CustomMessage.js' +import type { Command, CommandResponse } from './commandType/index.js' import type { InfoMessage } from './infoType/index.js' /** diff --git a/packages/core/src/core/types/EBMessageAddress.ts b/packages/core/src/core/types/EBMessageAddress.ts index 7b21e1936..512170158 100644 --- a/packages/core/src/core/types/EBMessageAddress.ts +++ b/packages/core/src/core/types/EBMessageAddress.ts @@ -4,12 +4,12 @@ import type { InstanceId } from './InstanceId.js' * A event bridge message address describes the sender or receiver of a message. */ export type EBMessageAddress = { - /** the name of the service */ - serviceName: Exclude - /** the version of the service */ - serviceVersion: Exclude - /** the name of the command or subscription */ - serviceTarget: Exclude - /** instance id of eventbridge */ - instanceId?: Exclude + /** the name of the service */ + serviceName: Exclude + /** the version of the service */ + serviceVersion: Exclude + /** the name of the command or subscription */ + serviceTarget: Exclude + /** instance id of eventbridge */ + instanceId?: Exclude } diff --git a/packages/core/src/core/types/EBMessageBase.ts b/packages/core/src/core/types/EBMessageBase.ts index 5efb65109..6eea933f4 100644 --- a/packages/core/src/core/types/EBMessageBase.ts +++ b/packages/core/src/core/types/EBMessageBase.ts @@ -10,25 +10,25 @@ import type { TraceId } from './TraceId.js' * Default fields which are part of any purista message */ export type EBMessageBase = { - /** global unique id of message */ - id: EBMessageId - /** timestamp of message creation time */ - timestamp: number - /** content type of message payload */ - contentType: ContentType - /** content encoding of message payload */ - contentEncoding: string - /** trace id of message */ - traceId?: TraceId - /** correlation id to know which command response referrs to which command */ - correlationId?: CorrelationId - /** principal id */ - principalId?: PrincipalId - /** principal id */ - tenantId?: TenantId - /** event name for this message */ - eventName?: string - /** stringified Opentelemetry parent trace id */ - otp?: string - sender: EBMessageSenderAddress + /** global unique id of message */ + id: EBMessageId + /** timestamp of message creation time */ + timestamp: number + /** content type of message payload */ + contentType: ContentType + /** content encoding of message payload */ + contentEncoding: string + /** trace id of message */ + traceId?: TraceId + /** correlation id to know which command response referrs to which command */ + correlationId?: CorrelationId + /** principal id */ + principalId?: PrincipalId + /** principal id */ + tenantId?: TenantId + /** event name for this message */ + eventName?: string + /** stringified Opentelemetry parent trace id */ + otp?: string + sender: EBMessageSenderAddress } diff --git a/packages/core/src/core/types/EBMessageSenderAddress.ts b/packages/core/src/core/types/EBMessageSenderAddress.ts index 31d2b3914..cafbda71b 100644 --- a/packages/core/src/core/types/EBMessageSenderAddress.ts +++ b/packages/core/src/core/types/EBMessageSenderAddress.ts @@ -5,5 +5,5 @@ import type { Prettify } from './Prettify.js' * A event bridge message address describes the sender or receiver of a message. */ export type EBMessageSenderAddress = Prettify< - Omit & Required> + Omit & Required> > diff --git a/packages/core/src/core/types/EBMessageType.enum.ts b/packages/core/src/core/types/EBMessageType.enum.ts index 7a7340dd7..3ae059b0d 100644 --- a/packages/core/src/core/types/EBMessageType.enum.ts +++ b/packages/core/src/core/types/EBMessageType.enum.ts @@ -2,42 +2,42 @@ * Type of event bridge message */ export enum EBMessageType { - /** - * Command message type: - * Message which is sent from a single sender to exactly one single receiver. - * The sender expects a answer response from receiver within a specific time frame (ttl). - * If the sender does not receive a answer within this time frame, the command will be rejected with timeout error. - */ - Command = 'command', // a message which expects an answer message from receiver + /** + * Command message type: + * Message which is sent from a single sender to exactly one single receiver. + * The sender expects a answer response from receiver within a specific time frame (ttl). + * If the sender does not receive a answer within this time frame, the command will be rejected with timeout error. + */ + Command = 'command', // a message which expects an answer message from receiver - /** a success response from receiver of a command message */ - CommandSuccessResponse = 'commandSuccessResponse', + /** a success response from receiver of a command message */ + CommandSuccessResponse = 'commandSuccessResponse', - /** a error response from receiver of a command message */ - CommandErrorResponse = 'commandErrorResponse', + /** a error response from receiver of a command message */ + CommandErrorResponse = 'commandErrorResponse', - /** - * Info message type: - * Message which is sent from a single sender to unspecified receivers. - * The sender does not expect any answer to this message and does not process any reply to this message. - * Info messages are fire & forget broadcasting messages. - */ - /** indicates that a service is booting */ - InfoServiceInit = 'infoServiceInit', - /** indicates that a service is ready */ - InfoServiceReady = 'infoServiceReady', - /** indicates that a service is not able to process requests (e.g. db not available) */ - InfoServiceNotReady = 'infoServiceNotReady', - /** send when a service provides a new function */ - InfoServiceFunctionAdded = 'infoServiceFunctionAdded', - /** indicates that a service is going to shut down and does no longer accept new requests */ - InfoServiceDrain = 'infoServiceDrain', - /** last event from service before service is destroyed */ - InfoServiceShutdown = 'infoServiceShutdown', - /** a service invoked a other function and did not get a answer within given ttl */ - InfoInvokeTimeout = 'infoInvokeTimeout', - /** a subscription function is throwing */ - InfoSubscriptionError = 'infoSubscriptionError', - /** a custom message / custom event */ - CustomMessage = 'customMessage', + /** + * Info message type: + * Message which is sent from a single sender to unspecified receivers. + * The sender does not expect any answer to this message and does not process any reply to this message. + * Info messages are fire & forget broadcasting messages. + */ + /** indicates that a service is booting */ + InfoServiceInit = 'infoServiceInit', + /** indicates that a service is ready */ + InfoServiceReady = 'infoServiceReady', + /** indicates that a service is not able to process requests (e.g. db not available) */ + InfoServiceNotReady = 'infoServiceNotReady', + /** send when a service provides a new function */ + InfoServiceFunctionAdded = 'infoServiceFunctionAdded', + /** indicates that a service is going to shut down and does no longer accept new requests */ + InfoServiceDrain = 'infoServiceDrain', + /** last event from service before service is destroyed */ + InfoServiceShutdown = 'infoServiceShutdown', + /** a service invoked a other function and did not get a answer within given ttl */ + InfoInvokeTimeout = 'infoInvokeTimeout', + /** a subscription function is throwing */ + InfoSubscriptionError = 'infoSubscriptionError', + /** a custom message / custom event */ + CustomMessage = 'customMessage', } diff --git a/packages/core/src/core/types/EmitCustomMessageFunction.ts b/packages/core/src/core/types/EmitCustomMessageFunction.ts index c903889a4..aba81d7a2 100644 --- a/packages/core/src/core/types/EmitCustomMessageFunction.ts +++ b/packages/core/src/core/types/EmitCustomMessageFunction.ts @@ -10,8 +10,8 @@ import type { EmitSchemaList } from './EmitSchemaList.js' * ``` */ export type EmitCustomMessageFunction = >( - eventName: K, - payload: EmitList[K], - contentType?: ContentType, - contentEncoding?: string, + eventName: K, + payload: EmitList[K], + contentType?: ContentType, + contentEncoding?: string, ) => Promise diff --git a/packages/core/src/core/types/EmitSchemaList.ts b/packages/core/src/core/types/EmitSchemaList.ts index 98e0225b7..a010b0d49 100644 --- a/packages/core/src/core/types/EmitSchemaList.ts +++ b/packages/core/src/core/types/EmitSchemaList.ts @@ -1,5 +1,5 @@ import type { Schema } from '@typeschema/main' export type EmitSchemaList = { - [K in keyof T]: Schema + [K in keyof T]: Schema } diff --git a/packages/core/src/core/types/EmptyObject.ts b/packages/core/src/core/types/EmptyObject.ts new file mode 100644 index 000000000..b97d61933 --- /dev/null +++ b/packages/core/src/core/types/EmptyObject.ts @@ -0,0 +1,2 @@ +// biome-ignore lint/complexity/noBannedTypes: Needed +export type EmptyObject = {} diff --git a/packages/core/src/core/types/ErrorResponsePayload.ts b/packages/core/src/core/types/ErrorResponsePayload.ts index 4a035b90a..31fdcbd4e 100644 --- a/packages/core/src/core/types/ErrorResponsePayload.ts +++ b/packages/core/src/core/types/ErrorResponsePayload.ts @@ -5,12 +5,12 @@ import type { TraceId } from './TraceId.js' * Error message payload */ export type ErrorResponsePayload = { - /** the error status code */ - status: StatusCode - /** a human readable error message */ - message: string - /** the trace if of the request */ - traceId?: TraceId - /** addition data */ - data?: unknown + /** the error status code */ + status: StatusCode + /** a human readable error message */ + message: string + /** the trace if of the request */ + traceId?: TraceId + /** addition data */ + data?: unknown } diff --git a/packages/core/src/core/types/FromEmitToOtherType.ts b/packages/core/src/core/types/FromEmitToOtherType.ts index 851439b17..54d93ff78 100644 --- a/packages/core/src/core/types/FromEmitToOtherType.ts +++ b/packages/core/src/core/types/FromEmitToOtherType.ts @@ -4,5 +4,5 @@ * serviceName.ServiceVersion.FunctionName becomes type of SinonStub */ export type FromEmitToOtherType = { - [TKey in keyof Entry]: NewType + [TKey in keyof Entry]: NewType } diff --git a/packages/core/src/core/types/FromInvokeToOtherType.ts b/packages/core/src/core/types/FromInvokeToOtherType.ts index a5af1eb7a..5f5ffae53 100644 --- a/packages/core/src/core/types/FromInvokeToOtherType.ts +++ b/packages/core/src/core/types/FromInvokeToOtherType.ts @@ -4,9 +4,9 @@ * serviceName.ServiceVersion.FunctionName becomes type of NewType */ export type FromInvokeToOtherType = { - [TKey in keyof Entry]: { - [TKey2 in keyof Entry[TKey]]: { - [TKey3 in keyof Entry[TKey][TKey2]]: NewType - } - } + [TKey in keyof Entry]: { + [TKey2 in keyof Entry[TKey]]: { + [TKey3 in keyof Entry[TKey][TKey2]]: NewType + } + } } diff --git a/packages/core/src/core/types/GenericEventEmitter.ts b/packages/core/src/core/types/GenericEventEmitter.ts index 14023b93b..a46926975 100644 --- a/packages/core/src/core/types/GenericEventEmitter.ts +++ b/packages/core/src/core/types/GenericEventEmitter.ts @@ -6,26 +6,26 @@ export type EventKey = string & keyof T type EventReceiver = (parameter: T) => void export interface IEmitter { - on>(eventName: K, fn: EventReceiver): void - off>(eventName: K, fn: EventReceiver): void - emit>(eventName: K, parameter?: T[K]): void + on>(eventName: K, fn: EventReceiver): void + off>(eventName: K, fn: EventReceiver): void + emit>(eventName: K, parameter?: T[K]): void } export class GenericEventEmitter implements IEmitter { - private emitter = new EventEmitter() - on>(eventName: K, fn: EventReceiver) { - this.emitter.on(eventName, fn) - } + private emitter = new EventEmitter() + on>(eventName: K, fn: EventReceiver) { + this.emitter.on(eventName, fn) + } - off>(eventName: K, fn: EventReceiver) { - this.emitter.off(eventName, fn) - } + off>(eventName: K, fn: EventReceiver) { + this.emitter.off(eventName, fn) + } - emit>(eventName: K, parameter?: T[K]) { - this.emitter.emit(eventName, parameter) - } + emit>(eventName: K, parameter?: T[K]) { + this.emitter.emit(eventName, parameter) + } - removeAllListeners() { - this.emitter.removeAllListeners() - } + removeAllListeners() { + this.emitter.removeAllListeners() + } } diff --git a/packages/core/src/core/types/GetMessageParamsType.ts b/packages/core/src/core/types/GetMessageParamsType.ts new file mode 100644 index 000000000..7f9cef503 --- /dev/null +++ b/packages/core/src/core/types/GetMessageParamsType.ts @@ -0,0 +1,10 @@ +import type { InferIn, Schema } from '@typeschema/main' + +export type GetMessageParamsType< + ParamsSchema extends Schema | undefined, + TransformInputParamsSchema extends Schema | undefined, +> = TransformInputParamsSchema extends Schema + ? InferIn + : ParamsSchema extends Schema + ? InferIn + : unknown diff --git a/packages/core/src/core/types/GetMessagePayloadType.ts b/packages/core/src/core/types/GetMessagePayloadType.ts new file mode 100644 index 000000000..4d3751eea --- /dev/null +++ b/packages/core/src/core/types/GetMessagePayloadType.ts @@ -0,0 +1,10 @@ +import type { InferIn, Schema } from '@typeschema/main' + +export type GetMessagePayloadType< + PayloadSchema extends Schema | undefined, + TransformInputPayloadSchema extends Schema | undefined, +> = TransformInputPayloadSchema extends Schema + ? InferIn + : PayloadSchema extends Schema + ? InferIn + : unknown diff --git a/packages/core/src/core/types/InferTypeOrEmptyObject.ts b/packages/core/src/core/types/InferTypeOrEmptyObject.ts new file mode 100644 index 000000000..d2d1e99d8 --- /dev/null +++ b/packages/core/src/core/types/InferTypeOrEmptyObject.ts @@ -0,0 +1,8 @@ +import type { Infer, Schema } from '@typeschema/main' +import type { EmptyObject } from './EmptyObject.js' + +export type InferTypeOrEmptyObject = T extends Schema + ? Infer extends EmptyObject + ? Infer + : EmptyObject + : EmptyObject diff --git a/packages/core/src/core/types/InvokeFunction.ts b/packages/core/src/core/types/InvokeFunction.ts index 6e4840bd3..5a45f0ea9 100644 --- a/packages/core/src/core/types/InvokeFunction.ts +++ b/packages/core/src/core/types/InvokeFunction.ts @@ -1,4 +1,5 @@ import type { EBMessageAddress } from './EBMessageAddress.js' +import type { EmptyObject } from './EmptyObject.js' /** * Invokes a command and returns the result. @@ -19,8 +20,12 @@ import type { EBMessageAddress } from './EBMessageAddress.js' * const result = await invoke(address, inputPayload inputParameter ) * ``` */ -export type InvokeFunction = ( - address: EBMessageAddress, - payload: PayloadType, - parameter: ParameterType, +export type InvokeFunction = < + InvokeResponseType = unknown, + PayloadType = unknown, + ParameterType extends EmptyObject = EmptyObject, +>( + address: EBMessageAddress, + payload: PayloadType, + parameter: ParameterType, ) => Promise diff --git a/packages/core/src/core/types/InvokeList.ts b/packages/core/src/core/types/InvokeList.ts new file mode 100644 index 000000000..24d0958b9 --- /dev/null +++ b/packages/core/src/core/types/InvokeList.ts @@ -0,0 +1,6 @@ +import type { Schema } from '@typeschema/main' + +export type InvokeList = Record< + string, + Record> +> diff --git a/packages/core/src/core/types/Logger.ts b/packages/core/src/core/types/Logger.ts index df58936fe..8201330a5 100644 --- a/packages/core/src/core/types/Logger.ts +++ b/packages/core/src/core/types/Logger.ts @@ -4,36 +4,36 @@ import type { TenantId } from './TenantId.js' import type { TraceId } from './TraceId.js' export type LoggerOptions = { - puristaVersion?: string - name?: string - module?: string - serviceVersion?: string - serviceName?: string - serviceTarget?: string - customTraceId?: TraceId - instanceId?: InstanceId - principalId?: PrincipalId - tenantId?: TenantId - hostname?: string + puristaVersion?: string + name?: string + module?: string + serviceVersion?: string + serviceName?: string + serviceTarget?: string + customTraceId?: TraceId + instanceId?: InstanceId + principalId?: PrincipalId + tenantId?: TenantId + hostname?: string } export type LogFnParamType = [unknown, string?, ...any] | [string, ...any] export abstract class Logger { - abstract info(...args: LogFnParamType): void - abstract fatal(...args: LogFnParamType): void - abstract error(...args: LogFnParamType): void - abstract warn(...args: LogFnParamType): void - abstract debug(...args: LogFnParamType): void - abstract trace(...args: LogFnParamType): void - abstract getChildLogger(options: LoggerOptions): Logger + abstract info(...args: LogFnParamType): void + abstract fatal(...args: LogFnParamType): void + abstract error(...args: LogFnParamType): void + abstract warn(...args: LogFnParamType): void + abstract debug(...args: LogFnParamType): void + abstract trace(...args: LogFnParamType): void + abstract getChildLogger(options: LoggerOptions): Logger } export interface ILogger { - info(...args: LogFnParamType): void - fatal(...args: LogFnParamType): void - error(...args: LogFnParamType): void - warn(...args: LogFnParamType): void - debug(...args: LogFnParamType): void - trace(...args: LogFnParamType): void + info(...args: LogFnParamType): void + fatal(...args: LogFnParamType): void + error(...args: LogFnParamType): void + warn(...args: LogFnParamType): void + debug(...args: LogFnParamType): void + trace(...args: LogFnParamType): void } diff --git a/packages/core/src/core/types/NeverObject.ts b/packages/core/src/core/types/NeverObject.ts new file mode 100644 index 000000000..edb7853e7 --- /dev/null +++ b/packages/core/src/core/types/NeverObject.ts @@ -0,0 +1 @@ +export type NeverObject = Record diff --git a/packages/core/src/core/types/Prettify.ts b/packages/core/src/core/types/Prettify.ts index 7ebc71ea5..b91c64a4f 100644 --- a/packages/core/src/core/types/Prettify.ts +++ b/packages/core/src/core/types/Prettify.ts @@ -1,3 +1,3 @@ export type Prettify = { - [K in keyof T]: T[K] + [K in keyof T]: T[K] } & {} diff --git a/packages/core/src/core/types/PuristaSpanName.enum.ts b/packages/core/src/core/types/PuristaSpanName.enum.ts index dd970198d..162d697eb 100644 --- a/packages/core/src/core/types/PuristaSpanName.enum.ts +++ b/packages/core/src/core/types/PuristaSpanName.enum.ts @@ -2,28 +2,28 @@ * Opentelemetry span names used by PURISTA framework */ export enum PuristaSpanName { - EventBridgeEmitMessage = 'purista.emit.MessageToBridge', - EventBridgeInvokeCommand = 'purista.command.invoke', - EventBridgeCommandResponse = 'purista.command.response', - EventBridgeHandleIncomingMessage = 'purista.handle.incomingMessage', + EventBridgeEmitMessage = 'purista.emit.MessageToBridge', + EventBridgeInvokeCommand = 'purista.command.invoke', + EventBridgeCommandResponse = 'purista.command.response', + EventBridgeHandleIncomingMessage = 'purista.handle.incomingMessage', - EventBridgeCommandSent = 'purista.command.sent', - EventBridgeCommandReceived = 'purista.command.received', - EventBridgeCommandResponseSent = 'purista.command.response.sent', - EventBridgeCommandResponseReceived = 'purista.command.response.received', - EventBridgeSubscriptionEventReceived = 'purista.subscription.eventReceived', + EventBridgeCommandSent = 'purista.command.sent', + EventBridgeCommandReceived = 'purista.command.received', + EventBridgeCommandResponseSent = 'purista.command.response.sent', + EventBridgeCommandResponseReceived = 'purista.command.response.received', + EventBridgeSubscriptionEventReceived = 'purista.subscription.eventReceived', - SecretStoreGetValue = 'purista.secretStore.getValue', - SecretStoreSetValue = 'purista.secretStore.setValue', - SecretStoreRemoveValue = 'purista.secretStore.removeValue', + SecretStoreGetValue = 'purista.secretStore.getValue', + SecretStoreSetValue = 'purista.secretStore.setValue', + SecretStoreRemoveValue = 'purista.secretStore.removeValue', - ConfigStoreGetValue = 'purista.configStore.getValue', - ConfigStoreSetValue = 'purista.configStore.setValue', - ConfigStoreRemoveValue = 'purista.configStore.removeValue', + ConfigStoreGetValue = 'purista.configStore.getValue', + ConfigStoreSetValue = 'purista.configStore.setValue', + ConfigStoreRemoveValue = 'purista.configStore.removeValue', - StateStoreGetValue = 'purista.stateStore.getValue', - StateStoreSetValue = 'purista.stateStore.setValue', - StateStoreRemoveValue = 'purista.stateStore.removeValue', + StateStoreGetValue = 'purista.stateStore.getValue', + StateStoreSetValue = 'purista.stateStore.setValue', + StateStoreRemoveValue = 'purista.stateStore.removeValue', - KubernetesHttpRequest = 'purist.kubernetes.HttpRequest', + KubernetesHttpRequest = 'purist.kubernetes.HttpRequest', } diff --git a/packages/core/src/core/types/PuristaSpanTag.enum.ts b/packages/core/src/core/types/PuristaSpanTag.enum.ts index ddfe1d59f..1adaba375 100644 --- a/packages/core/src/core/types/PuristaSpanTag.enum.ts +++ b/packages/core/src/core/types/PuristaSpanTag.enum.ts @@ -2,17 +2,17 @@ * Opentelemetry tags set by PURISTA framework */ export enum PuristaSpanTag { - PuristaVersion = 'purista.version', - PrincipalId = 'purista.principalId', - TenantId = 'purista.tenantId', - SenderServiceName = 'purista.sender.name', - SenderServiceVersion = 'purista.sender.version', - SenderServiceTarget = 'purista.sender.target', + PuristaVersion = 'purista.version', + PrincipalId = 'purista.principalId', + TenantId = 'purista.tenantId', + SenderServiceName = 'purista.sender.name', + SenderServiceVersion = 'purista.sender.version', + SenderServiceTarget = 'purista.sender.target', - ReceiverServiceName = 'purista.receiver.name', - ReceiverServiceVersion = 'purista.receiver.version', - ReceiverServiceTarget = 'purista.receiver.target', + ReceiverServiceName = 'purista.receiver.name', + ReceiverServiceVersion = 'purista.receiver.version', + ReceiverServiceTarget = 'purista.receiver.target', - StoreType = 'purista.store.type', - StoreName = 'purista.store.name', + StoreType = 'purista.store.type', + StoreName = 'purista.store.name', } diff --git a/packages/core/src/core/types/ServiceBuilderTypes.ts b/packages/core/src/core/types/ServiceBuilderTypes.ts new file mode 100644 index 000000000..01b4fa459 --- /dev/null +++ b/packages/core/src/core/types/ServiceBuilderTypes.ts @@ -0,0 +1,18 @@ +import type { Service } from '../Service/Service.impl.js' +import type { EmptyObject } from './EmptyObject.js' +import type { ServiceClass } from './ServiceClass.js' +import type { ServiceClassTypes } from './ServiceClassTypes.js' + +export type ServiceBuilderTypes< + ConfigType extends {} = EmptyObject, + ConfigInputType extends {} = EmptyObject, + Resources extends {} = EmptyObject, + ServiceClassType extends ServiceClass> = Service< + ServiceClassTypes + >, +> = { + ConfigType: ConfigType + ConfigInputType: ConfigInputType + Resources: Resources + ServiceClassType: ServiceClassType +} diff --git a/packages/core/src/core/types/ServiceClass.ts b/packages/core/src/core/types/ServiceClass.ts index dcd67a7e6..fe34d329f 100644 --- a/packages/core/src/core/types/ServiceClass.ts +++ b/packages/core/src/core/types/ServiceClass.ts @@ -1,5 +1,7 @@ -import type { Context, Span, SpanOptions, Tracer } from '@opentelemetry/api' +import type { Context, Span, SpanOptions } from '@opentelemetry/api' +import type { Tracer } from '@opentelemetry/sdk-trace-node' +import type { ServiceClassTypes } from './ServiceClassTypes.js' import type { CommandDefinition } from './commandType/index.js' import type { SubscriptionDefinition } from './subscription/index.js' @@ -8,60 +10,65 @@ import type { SubscriptionDefinition } from './subscription/index.js' * * @group Service */ -export interface ServiceClass { - config: ConfigType +export interface ServiceClass { + config: S['ConfigType'] + resources: S['Resources'] - /** - * Stop and destroy the current service - */ - destroy(): Promise + /** + * Stop and destroy the current service + */ + destroy(): Promise - /** - * Start the service - */ - start(): Promise + /** + * Start the service + */ + start(): Promise - /** - * Wrap the given function in a opententelemetry span. - * The span will be on same hierarchy level as the current span. - * - * @param name the name of the span - * @param opts the additional span options - * @param fn the function to be wrapped in span - * @param context the span context - */ - wrapInSpan(name: string, opts: SpanOptions, fn: (span: Span) => Promise, context?: Context): Promise + /** + * Wrap the given function in a opententelemetry span. + * The span will be on same hierarchy level as the current span. + * + * @param name the name of the span + * @param opts the additional span options + * @param fn the function to be wrapped in span + * @param context the span context + */ + wrapInSpan(name: string, opts: SpanOptions, fn: (span: Span) => Promise, context?: Context): Promise - /** - * Start a new active opentelemetry span with given options. - * A active span will be below the current span in hierarchy - * - * @param name the name of the span - * @param opts the additional span options - * @param context the span context - * @param fn the function to be wrapped into the span - */ - startActiveSpan( - name: string, - opts: SpanOptions, - context: Context | undefined, - fn: (span: Span) => Promise, - ): Promise + /** + * Start a new active opentelemetry span with given options. + * A active span will be below the current span in hierarchy + * + * @param name the name of the span + * @param opts the additional span options + * @param context the span context + * @param fn the function to be wrapped into the span + */ + startActiveSpan( + name: string, + opts: SpanOptions, + context: Context | undefined, + fn: (span: Span) => Promise, + ): Promise - /** - * get the opentelemetry tracer of the service - */ - getTracer(): Tracer + /** + * get the opentelemetry tracer of the service + */ + getTracer(): Tracer - /** - * Registers a new command for the service - * @param commandDefinition the service command definition - */ - registerCommand(commandDefinition: CommandDefinition): Promise + /** + * Registers a new command for the service + * @param commandDefinition the service command definition + */ + registerCommand( + commandDefinition: CommandDefinition, + ): Promise - /** - * Registers a new subscription for the service - * @param subscriptionDefinition the subscription definition - */ - registerSubscription(subscriptionDefinition: SubscriptionDefinition): Promise + /** + * Registers a new subscription for the service + * @param subscriptionDefinition the subscription definition + */ + registerSubscription( + subscriptionDefinition: SubscriptionDefinition, + ): Promise } diff --git a/packages/core/src/core/types/ServiceClassTypes.ts b/packages/core/src/core/types/ServiceClassTypes.ts new file mode 100644 index 000000000..be45a5b7e --- /dev/null +++ b/packages/core/src/core/types/ServiceClassTypes.ts @@ -0,0 +1,6 @@ +import type { EmptyObject } from './EmptyObject.js' + +export type ServiceClassTypes = { + ConfigType: ConfigType + Resources: Resources +} diff --git a/packages/core/src/core/types/ServiceConstructorInput.ts b/packages/core/src/core/types/ServiceConstructorInput.ts index 44b9e9462..102cb5984 100644 --- a/packages/core/src/core/types/ServiceConstructorInput.ts +++ b/packages/core/src/core/types/ServiceConstructorInput.ts @@ -5,35 +5,37 @@ import type { ConfigStore } from '../ConfigStore/index.js' import type { EventBridge } from '../EventBridge/index.js' import type { SecretStore } from '../SecretStore/index.js' import type { StateStore } from '../StateStore/index.js' +import type { Logger } from './Logger.js' +import type { ServiceClassTypes } from './ServiceClassTypes.js' import type { CommandDefinitionListResolved } from './commandType/index.js' import type { ServiceInfoType } from './infoType/index.js' -import type { Logger } from './Logger.js' import type { SubscriptionDefinitionListResolved } from './subscription/index.js' /** * @group Service */ -export type ServiceConstructorInput = { - /** A logger instance */ - logger: Logger - /** The service info with name, version and description of service */ - info: ServiceInfoType - /** The eventBridge instance */ - eventBridge: EventBridge - /** The list of command definitions for this service */ - commandDefinitionList: CommandDefinitionListResolved - /** The list of subscription definitions for this service */ - subscriptionDefinitionList: SubscriptionDefinitionListResolved - /** The service specific config */ - config: ConfigType - /** The secret store instance */ - secretStore?: SecretStore - /** The config store instance */ - configStore?: ConfigStore - /** the state store instance */ - stateStore?: StateStore - /** The opentelemetry span processor instance */ - spanProcessor?: SpanProcessor - /** The config validation schema */ - configSchema?: Schema +export type ServiceConstructorInput = { + /** A logger instance */ + logger: Logger + /** The service info with name, version and description of service */ + info: ServiceInfoType + /** The eventBridge instance */ + eventBridge: EventBridge + /** The list of command definitions for this service */ + commandDefinitionList: CommandDefinitionListResolved + /** The list of subscription definitions for this service */ + subscriptionDefinitionList: SubscriptionDefinitionListResolved + /** The service specific config */ + config: S['ConfigType'] + /** The secret store instance */ + secretStore?: SecretStore + /** The config store instance */ + configStore?: ConfigStore + /** the state store instance */ + stateStore?: StateStore + /** The opentelemetry span processor instance */ + spanProcessor?: SpanProcessor + /** The config validation schema */ + configSchema?: Schema + resources?: S['Resources'] } diff --git a/packages/core/src/core/types/ServiceEvents.ts b/packages/core/src/core/types/ServiceEvents.ts index 8ba978692..0cea6a543 100644 --- a/packages/core/src/core/types/ServiceEvents.ts +++ b/packages/core/src/core/types/ServiceEvents.ts @@ -1,6 +1,6 @@ import type { HandledError } from '../Error/HandledError.impl.js' -import type { addPrefixToObject } from './addPrefixToObject.js' import type { TraceId } from './TraceId.js' +import type { addPrefixToObject } from './addPrefixToObject.js' /** * Events which can be emitted by a service. @@ -9,37 +9,37 @@ import type { TraceId } from './TraceId.js' * @group Service */ export enum ServiceEventsNames { - /** emitted when the service is started, but not fully initialized and not ready yet @event */ - ServiceStarted = 'service-started', + /** emitted when the service is started, but not fully initialized and not ready yet @event */ + ServiceStarted = 'service-started', - /** - * emitted when the service is fully initialized and ready - * Should be emitted by custom service class. - * It is not emitted by default - * @event - */ - ServiceAvailable = 'service-available', + /** + * emitted when the service is fully initialized and ready + * Should be emitted by custom service class. + * It is not emitted by default + * @event + */ + ServiceAvailable = 'service-available', - /** emitted when the service is going to be stopped @event */ - ServiceDrain = 'service-drain', + /** emitted when the service is going to be stopped @event */ + ServiceDrain = 'service-drain', - /** emitted when the service has been stopped @event */ - ServiceStopped = 'service-stopped', + /** emitted when the service has been stopped @event */ + ServiceStopped = 'service-stopped', - /** emitted when the service is not available (for example database connection could not be established) @event */ - ServiceUnavailable = 'service-not-available', + /** emitted when the service is not available (for example database connection could not be established) @event */ + ServiceUnavailable = 'service-not-available', - /** emitted when a subscription throws a HandledError @event */ - SubscriptionHandledError = 'service-handled-subscription-error', + /** emitted when a subscription throws a HandledError @event */ + SubscriptionHandledError = 'service-handled-subscription-error', - /** emitted when a command throws a HandledError @event */ - CommandHandledError = 'service-handled-command-error', + /** emitted when a command throws a HandledError @event */ + CommandHandledError = 'service-handled-command-error', - /** emitted when a subscription throws an error other than a HandledError @event */ - SubscriptionUnhandledError = 'service-unhandled-subscription-error', + /** emitted when a subscription throws an error other than a HandledError @event */ + SubscriptionUnhandledError = 'service-unhandled-subscription-error', - /** emitted when a command throws an error other than a HandledError @event */ - CommandUnhandledError = 'service-unhandled-command-error', + /** emitted when a command throws an error other than a HandledError @event */ + CommandUnhandledError = 'service-unhandled-command-error', } /** @@ -52,40 +52,40 @@ export enum ServiceEventsNames { * @group Service */ export type ServiceEventsInternal = { - /** emitted when the service is started, but not fully initialized and not ready yet */ - [ServiceEventsNames.ServiceStarted]: undefined + /** emitted when the service is started, but not fully initialized and not ready yet */ + [ServiceEventsNames.ServiceStarted]: undefined - /** - * emitted when the service is fully initialized and ready - * Should be emitted by custom service class. - * It is not emitted by default - */ - [ServiceEventsNames.ServiceAvailable]: undefined + /** + * emitted when the service is fully initialized and ready + * Should be emitted by custom service class. + * It is not emitted by default + */ + [ServiceEventsNames.ServiceAvailable]: undefined - /** emitted when the service is going to be stopped */ - [ServiceEventsNames.ServiceDrain]: undefined + /** emitted when the service is going to be stopped */ + [ServiceEventsNames.ServiceDrain]: undefined - /** emitted when the service has been stopped */ - [ServiceEventsNames.ServiceStopped]: undefined + /** emitted when the service has been stopped */ + [ServiceEventsNames.ServiceStopped]: undefined - /** emitted when the service is not available (for example database connection could not be established) */ - [ServiceEventsNames.ServiceUnavailable]: undefined | unknown + /** emitted when the service is not available (for example database connection could not be established) */ + [ServiceEventsNames.ServiceUnavailable]: undefined | unknown - /** emitted when a subscription throws a HandledError */ - [ServiceEventsNames.SubscriptionHandledError]: { subscriptionName: string; error: HandledError; traceId?: TraceId } + /** emitted when a subscription throws a HandledError */ + [ServiceEventsNames.SubscriptionHandledError]: { subscriptionName: string; error: HandledError; traceId?: TraceId } - /** emitted when a command throws a HandledError */ - [ServiceEventsNames.CommandHandledError]: { commandName: string; error: HandledError; traceId?: TraceId } + /** emitted when a command throws a HandledError */ + [ServiceEventsNames.CommandHandledError]: { commandName: string; error: HandledError; traceId?: TraceId } - /** emitted when a subscription throws an error other than a HandledError */ - [ServiceEventsNames.SubscriptionUnhandledError]: { subscriptionName: string; error: unknown; traceId?: TraceId } + /** emitted when a subscription throws an error other than a HandledError */ + [ServiceEventsNames.SubscriptionUnhandledError]: { subscriptionName: string; error: unknown; traceId?: TraceId } - /** emitted when a command throws an error other than a HandledError */ - [ServiceEventsNames.CommandUnhandledError]: { commandName: string; error: unknown; traceId?: TraceId } + /** emitted when a command throws an error other than a HandledError */ + [ServiceEventsNames.CommandUnhandledError]: { commandName: string; error: unknown; traceId?: TraceId } } type CustomEvents = { - [key: string]: unknown + [key: string]: unknown } /** @@ -96,5 +96,5 @@ type CustomEvents = { * - additional events, free defined by developer are prefixed with `misc-` */ export type ServiceEvents = ServiceEventsInternal & - addPrefixToObject & - addPrefixToObject + addPrefixToObject & + addPrefixToObject diff --git a/packages/core/src/core/types/SetNewTypeValue.ts b/packages/core/src/core/types/SetNewTypeValue.ts new file mode 100644 index 000000000..06e7954d7 --- /dev/null +++ b/packages/core/src/core/types/SetNewTypeValue.ts @@ -0,0 +1,7 @@ +export type SetNewTypeValue = { + [P in keyof T]: P extends K ? V : T[P] +} + +export type SetNewTypeValues = { + [P in keyof T]: P extends keyof Changes ? Changes[P] : T[P] +} diff --git a/packages/core/src/core/types/StatusCode.enum.ts b/packages/core/src/core/types/StatusCode.enum.ts index 4315c0682..bb039bfd1 100644 --- a/packages/core/src/core/types/StatusCode.enum.ts +++ b/packages/core/src/core/types/StatusCode.enum.ts @@ -3,71 +3,71 @@ * The codes are based on HTTP status codes */ export enum StatusCode { - InfoContinue = 100, - InfoSwitchingProtocols = 101, - InfoProcessing = 102, - OK = 200, - Created = 201, - Accepted = 202, - NonAuthoritativeInfo = 203, - NoContent = 204, - ResetContent = 205, - PartialContent = 206, - MultiStatus = 207, - AlreadyReported = 208, - IMUsed = 229, - RedirectMultipleChoices = 300, - RedirectMovedPermanently = 301, - RedirectFound = 302, + InfoContinue = 100, + InfoSwitchingProtocols = 101, + InfoProcessing = 102, + OK = 200, + Created = 201, + Accepted = 202, + NonAuthoritativeInfo = 203, + NoContent = 204, + ResetContent = 205, + PartialContent = 206, + MultiStatus = 207, + AlreadyReported = 208, + IMUsed = 229, + RedirectMultipleChoices = 300, + RedirectMovedPermanently = 301, + RedirectFound = 302, - RedirectSeeOther = 303, - RedirectNotModified = 304, - RedirectUseProxy = 305, + RedirectSeeOther = 303, + RedirectNotModified = 304, + RedirectUseProxy = 305, - RedirectSwitchProxy = 306, - RedirectTemp = 307, - RedirectPermanent = 308, - BadRequest = 400, - Unauthorized = 401, - PaymentRequired = 402, - Forbidden = 403, - NotFound = 404, - MethodNotAllowed = 405, - NotAcceptable = 406, - ProxyAuthRequired = 407, - RequestTimeout = 408, - Conflict = 409, - Gone = 410, - LengthRequired = 411, - PreconditionFailed = 412, - PayloadTooLarge = 413, - URITooLong = 414, - UnsupportedMediaType = 415, - RangeNotSatisfiable = 416, - ExpectationFailed = 417, - ImATeapot = 418, - MisdirectedRequest = 421, - UnprocessableEntity = 422, - Locked = 423, - UpgradeRequired = 426, - PreconditionRequired = 428, - TooManyRequests = 429, - RequestHeaderFieldsTooLarge = 431, - LoginTimeOut = 440, - RetryWith = 449, - UnavailableForLegalReasons = 451, - InvalidToken = 498, - TokenRequired = 499, - InternalServerError = 500, - NotImplemented = 501, - BadGateway = 502, - ServiceUnavailable = 503, - GatewayTimeout = 504, - HTTPVersionNotSupported = 505, - VariantAlsoNegotiates = 506, - InsufficientStorage = 507, - LoopDetected = 508, - BandwidthLimitExceeded = 509, - NotExtended = 510, - NetworkAuthRequired = 511, + RedirectSwitchProxy = 306, + RedirectTemp = 307, + RedirectPermanent = 308, + BadRequest = 400, + Unauthorized = 401, + PaymentRequired = 402, + Forbidden = 403, + NotFound = 404, + MethodNotAllowed = 405, + NotAcceptable = 406, + ProxyAuthRequired = 407, + RequestTimeout = 408, + Conflict = 409, + Gone = 410, + LengthRequired = 411, + PreconditionFailed = 412, + PayloadTooLarge = 413, + URITooLong = 414, + UnsupportedMediaType = 415, + RangeNotSatisfiable = 416, + ExpectationFailed = 417, + ImATeapot = 418, + MisdirectedRequest = 421, + UnprocessableEntity = 422, + Locked = 423, + UpgradeRequired = 426, + PreconditionRequired = 428, + TooManyRequests = 429, + RequestHeaderFieldsTooLarge = 431, + LoginTimeOut = 440, + RetryWith = 449, + UnavailableForLegalReasons = 451, + InvalidToken = 498, + TokenRequired = 499, + InternalServerError = 500, + NotImplemented = 501, + BadGateway = 502, + ServiceUnavailable = 503, + GatewayTimeout = 504, + HTTPVersionNotSupported = 505, + VariantAlsoNegotiates = 506, + InsufficientStorage = 507, + LoopDetected = 508, + BandwidthLimitExceeded = 509, + NotExtended = 510, + NetworkAuthRequired = 511, } diff --git a/packages/core/src/core/types/StoreBaseConfig.ts b/packages/core/src/core/types/StoreBaseConfig.ts index 290513201..a818393f4 100644 --- a/packages/core/src/core/types/StoreBaseConfig.ts +++ b/packages/core/src/core/types/StoreBaseConfig.ts @@ -1,41 +1,41 @@ -import type { Logger } from './Logger.js' import type { LogLevelName } from './LogLevelName.js' +import type { Logger } from './Logger.js' import type { Prettify } from './Prettify.js' /** * Basic configuration object which is used by any store */ export type StoreBaseConfig> = Prettify< - { - /** - * Enable generally get method - */ - enableGet?: boolean - /** - * Enable generally set method - */ - enableSet?: boolean - /** - * Enable generally remove method - */ - enableRemove?: boolean - /** - * A logger instance - */ - logger?: Logger - /** - * A log level for new logger if logger is not set - */ - logLevel?: LogLevelName + { + /** + * Enable generally get method + */ + enableGet?: boolean + /** + * Enable generally set method + */ + enableSet?: boolean + /** + * Enable generally remove method + */ + enableRemove?: boolean + /** + * A logger instance + */ + logger?: Logger + /** + * A log level for new logger if logger is not set + */ + logLevel?: LogLevelName - /** - * Enable cache - */ - enableCache?: boolean + /** + * Enable cache + */ + enableCache?: boolean - /** - * Cache time to live in ms - */ - cacheTtl?: number - } & Config + /** + * Cache time to live in ms + */ + cacheTtl?: number + } & Config > diff --git a/packages/core/src/core/types/StoreType.enum.ts b/packages/core/src/core/types/StoreType.enum.ts index 7314e5e6d..2f4c9384e 100644 --- a/packages/core/src/core/types/StoreType.enum.ts +++ b/packages/core/src/core/types/StoreType.enum.ts @@ -1,5 +1,5 @@ export enum StoreType { - StateStore = 'StateStore', - ConfigStore = 'ConfigStore', - SecretStore = 'SecretStore', + StateStore = 'StateStore', + ConfigStore = 'ConfigStore', + SecretStore = 'SecretStore', } diff --git a/packages/core/src/core/types/addPrefixToObject.ts b/packages/core/src/core/types/addPrefixToObject.ts index 8f7082dd6..0e28cd8ac 100644 --- a/packages/core/src/core/types/addPrefixToObject.ts +++ b/packages/core/src/core/types/addPrefixToObject.ts @@ -5,5 +5,5 @@ * ``` */ export type addPrefixToObject = { - [K in keyof T as K extends string ? `${P}${K}` : never]: T[K] + [K in keyof T as K extends string ? `${P}${K}` : never]: T[K] } diff --git a/packages/core/src/core/types/commandType/Command.ts b/packages/core/src/core/types/commandType/Command.ts index 2dfbb8429..c0fb82a5b 100644 --- a/packages/core/src/core/types/commandType/Command.ts +++ b/packages/core/src/core/types/commandType/Command.ts @@ -16,13 +16,13 @@ import type { Prettify } from '../Prettify.js' * Subscribers should not respond with command responses if they are "silent" subscribers/listeners. */ export type Command = Prettify< - { - messageType: EBMessageType.Command - correlationId: CorrelationId - receiver: EBMessageAddress - payload: { - parameter: ParameterType - payload: PayloadType - } - } & EBMessageBase + { + messageType: EBMessageType.Command + correlationId: CorrelationId + receiver: EBMessageAddress + payload: { + parameter: ParameterType + payload: PayloadType + } + } & EBMessageBase > diff --git a/packages/core/src/core/types/commandType/CommandAfterGuardHook.ts b/packages/core/src/core/types/commandType/CommandAfterGuardHook.ts index 0b4645b66..29ab0df0d 100644 --- a/packages/core/src/core/types/commandType/CommandAfterGuardHook.ts +++ b/packages/core/src/core/types/commandType/CommandAfterGuardHook.ts @@ -1,3 +1,6 @@ +import type { Schema } from '@typeschema/main' +import type { EmptyObject } from '../EmptyObject.js' +import type { InvokeList } from '../InvokeList.js' import type { ServiceClass } from '../ServiceClass.js' import type { CommandFunctionContext } from './CommandFunctionContext.js' @@ -8,18 +11,19 @@ import type { CommandFunctionContext } from './CommandFunctionContext.js' * @group Command */ export type CommandAfterGuardHook< - ServiceClassType = ServiceClass, - MessagePayloadType = unknown, - MessageParamsType = unknown, - FunctionResultType = unknown, - FunctionPayloadType = unknown, - FunctionParamsType = unknown, - Invokes = {}, - EmitListType = {}, + S extends ServiceClass = ServiceClass, + MessagePayloadType = unknown, + MessageParamsType = unknown, + FunctionPayloadType = unknown, + FunctionParamsType = unknown, + FunctionOutputType = unknown, + Resources extends Record = EmptyObject, + Invokes extends InvokeList = EmptyObject, + EmitList extends Record = EmptyObject, > = ( - this: ServiceClassType, - context: CommandFunctionContext, - result: Readonly, - input: Readonly, - parameter: Readonly, + this: S, + context: CommandFunctionContext, + result: Readonly, + originalPayload: Readonly, + originalParameter: Readonly, ) => Promise diff --git a/packages/core/src/core/types/commandType/CommandBeforeGuardHook.ts b/packages/core/src/core/types/commandType/CommandBeforeGuardHook.ts index bce9d5714..fc6969373 100644 --- a/packages/core/src/core/types/commandType/CommandBeforeGuardHook.ts +++ b/packages/core/src/core/types/commandType/CommandBeforeGuardHook.ts @@ -1,4 +1,6 @@ -import type { ServiceClass } from '../ServiceClass.js' +import type { Schema } from '@typeschema/main' +import type { EmptyObject, InvokeList, Service } from '../../index.js' + import type { CommandFunctionContext } from './CommandFunctionContext.js' /** @@ -9,16 +11,17 @@ import type { CommandFunctionContext } from './CommandFunctionContext.js' * @group Command */ export type CommandBeforeGuardHook< - ServiceClassType = ServiceClass, - MessagePayloadType = unknown, - MessageParamsType = unknown, - FunctionPayloadType = MessagePayloadType, - FunctionParamsType = MessageParamsType, - Invokes = {}, - EmitListType = {}, + S extends Service = Service, + MessagePayloadType = unknown, + MessageParamsType = unknown, + FunctionPayloadType = unknown, + FunctionParamsType = unknown, + Resources extends Record = EmptyObject, + Invokes extends InvokeList = EmptyObject, + EmitList extends Record = EmptyObject, > = ( - this: ServiceClassType, - context: CommandFunctionContext, - payload: Readonly, - parameter: Readonly, + this: S, + context: CommandFunctionContext, + payload: Readonly, + parameter: Readonly, ) => Promise diff --git a/packages/core/src/core/types/commandType/CommandDefinition.ts b/packages/core/src/core/types/commandType/CommandDefinition.ts index e7c462010..a2c335c22 100644 --- a/packages/core/src/core/types/commandType/CommandDefinition.ts +++ b/packages/core/src/core/types/commandType/CommandDefinition.ts @@ -1,8 +1,10 @@ import type { Schema } from '@typeschema/main' +import type { SchemaObject } from 'openapi3-ts/oas31' +import type { InvokeList, Service } from '../../index.js' import type { DefinitionEventBridgeConfig } from '../DefinitionEventBridgeConfig.js' +import type { FromEmitToOtherType } from '../FromEmitToOtherType.js' import type { FromInvokeToOtherType } from '../FromInvokeToOtherType.js' -import type { ServiceClass } from '../ServiceClass.js' import type { CommandAfterGuardHook } from './CommandAfterGuardHook.js' import type { CommandBeforeGuardHook } from './CommandBeforeGuardHook.js' import type { CommandDefinitionMetadataBase } from './CommandDefinitionMetadataBase.js' @@ -16,82 +18,100 @@ import type { CommandTransformOutputHook } from './CommandTransformOutputHook.js * @group Command */ export type CommandDefinition< - ServiceClassType extends ServiceClass = ServiceClass, - MetadataType = CommandDefinitionMetadataBase, - MessagePayloadType = unknown, - MessageParamsType = unknown, - MessageResultType = unknown, - FunctionPayloadType = MessagePayloadType, - FunctionParamsType = MessageParamsType, - FunctionResultType = unknown, - Invokes = {}, - EmitListType = {}, + S extends Service, + MessagePayloadType, + MessageParamsType, + TransformInputPayload, + TransformInputParams, + FunctionPayloadType, + FunctionParamsType, + FunctionOutputType, + FinalFunctionOutputType, + TransformOutputHookOutput, + Resources extends Record, + Invokes extends InvokeList, + EmitList extends Record, + MetadataType extends CommandDefinitionMetadataBase = CommandDefinitionMetadataBase, > = { - /** the name of the command */ - commandName: string - /** the description of the command */ - commandDescription: string - /** the metadata of the command */ - metadata: MetadataType - /** config information for event bridge */ - eventBridgeConfig: DefinitionEventBridgeConfig - /** the command function */ - call: CommandFunction< - ServiceClassType, - MessagePayloadType, - MessageParamsType, - FunctionPayloadType, - FunctionParamsType, - FunctionResultType, - Invokes, - EmitListType - > - /** the eventName for the command response */ - eventName?: string - /** hooks of command */ - hooks: { - transformInput?: { - transformInputSchema: Schema - transformParameterSchema: Schema - transformFunction: CommandTransformInputHook - } - beforeGuard?: Record< - string, - CommandBeforeGuardHook< - ServiceClassType, - MessagePayloadType, - MessageParamsType, - FunctionPayloadType, - FunctionParamsType, - Invokes, - EmitListType - > - > - afterGuard?: Record< - string, - CommandAfterGuardHook< - ServiceClassType, - MessagePayloadType, - MessageParamsType, - FunctionResultType, - FunctionPayloadType, - FunctionParamsType, - Invokes, - EmitListType - > - > - transformOutput?: { - transformOutputSchema: Schema - transformFunction: CommandTransformOutputHook< - ServiceClassType, - MessagePayloadType, - MessageParamsType, - MessageResultType, - FunctionResultType, - FunctionParamsType - > - } - } - invokes: FromInvokeToOtherType - emitList: EmitListType + /** the name of the command */ + commandName: string + /** the description of the command */ + commandDescription: string + /** the metadata of the command */ + metadata: MetadataType + /** config information for event bridge */ + eventBridgeConfig: DefinitionEventBridgeConfig + /** the command function */ + call: CommandFunction< + S, + MessagePayloadType, + MessageParamsType, + FunctionPayloadType, + FunctionParamsType, + FunctionOutputType, + Resources, + Invokes, + EmitList + > + /** the eventName for the command response */ + eventName?: string + /** hooks of command */ + hooks: { + transformInput?: { + transformInputSchema: Schema + transformParameterSchema: Schema + transformFunction: CommandTransformInputHook< + S, + MessagePayloadType, + MessageParamsType, + TransformInputPayload, + TransformInputParams, + FunctionPayloadType, + FunctionParamsType + > + } + beforeGuard?: Record< + string, + CommandBeforeGuardHook< + S, + MessagePayloadType, + MessageParamsType, + FunctionPayloadType, + FunctionParamsType, + Resources, + Invokes, + EmitList + > + > + afterGuard?: Record< + string, + CommandAfterGuardHook< + S, + MessagePayloadType, + MessageParamsType, + FunctionPayloadType, + FunctionParamsType, + FunctionOutputType, + Resources, + Invokes, + EmitList + > + > + transformOutput?: { + transformOutputSchema: Schema + transformFunction: CommandTransformOutputHook< + S, + MessagePayloadType, + MessageParamsType, + FinalFunctionOutputType, + FunctionParamsType, + TransformOutputHookOutput + > + } + } + invokes: FromInvokeToOtherType< + Invokes, + { outputSchema?: SchemaObject; payloadSchema?: SchemaObject; parameterSchema?: SchemaObject } + > + emitList: FromEmitToOtherType } diff --git a/packages/core/src/core/types/commandType/CommandDefinitionList.ts b/packages/core/src/core/types/commandType/CommandDefinitionList.ts index 1a99e1f22..37f8df79c 100644 --- a/packages/core/src/core/types/commandType/CommandDefinitionList.ts +++ b/packages/core/src/core/types/commandType/CommandDefinitionList.ts @@ -1,4 +1,4 @@ -import type { ServiceClass } from '../ServiceClass.js' +import type { Service } from '../../Service/index.js' import type { CommandDefinition } from './CommandDefinition.js' import type { CommandDefinitionMetadataBase } from './CommandDefinitionMetadataBase.js' @@ -9,19 +9,23 @@ import type { CommandDefinitionMetadataBase } from './CommandDefinitionMetadataB * export const userServiceCommands: CommandDefinitionList = [signUp.getDefinition()] * ``` */ -export type CommandDefinitionList = Promise< - CommandDefinition +export type CommandDefinitionList = Promise< + CommandDefinition >[] -export type CommandDefinitionListResolved = CommandDefinition< - ServiceClassType, - CommandDefinitionMetadataBase, - any, - any, - any, - any, - any, - any, - any, - any +export type CommandDefinitionListResolved = CommandDefinition< + S, + any, + any, + any, + any, + any, + any, + any, + any, + any, + any, + any, + any, + CommandDefinitionMetadataBase >[] diff --git a/packages/core/src/core/types/commandType/CommandDefinitionMetadataBase.ts b/packages/core/src/core/types/commandType/CommandDefinitionMetadataBase.ts index 113964a24..15b0a376e 100644 --- a/packages/core/src/core/types/commandType/CommandDefinitionMetadataBase.ts +++ b/packages/core/src/core/types/commandType/CommandDefinitionMetadataBase.ts @@ -3,14 +3,14 @@ import type { SchemaObject } from 'openapi3-ts/oas31' import type { ContentType } from '../ContentType.js' export type CommandDefinitionMetadataBase = { - expose: { - contentTypeRequest?: ContentType - contentEncodingRequest?: string - contentTypeResponse?: ContentType - contentEncodingResponse?: string - inputPayload?: SchemaObject - outputPayload?: SchemaObject - parameter?: SchemaObject - deprecated?: boolean - } + expose: { + contentTypeRequest?: ContentType + contentEncodingRequest?: string + contentTypeResponse?: ContentType + contentEncodingResponse?: string + inputPayload?: SchemaObject + outputPayload?: SchemaObject + parameter?: SchemaObject + deprecated?: boolean + } } diff --git a/packages/core/src/core/types/commandType/CommandErrorResponse.ts b/packages/core/src/core/types/commandType/CommandErrorResponse.ts index 243b08b3e..65fc1f76f 100644 --- a/packages/core/src/core/types/commandType/CommandErrorResponse.ts +++ b/packages/core/src/core/types/commandType/CommandErrorResponse.ts @@ -11,17 +11,17 @@ import type { StatusCode } from '../StatusCode.enum.js' * @group Command */ export type CommandErrorResponse = Prettify< - { - messageType: EBMessageType.CommandErrorResponse - contentType: 'application/json' - contentEncoding: 'utf-8' - isHandledError: boolean - correlationId: CorrelationId - receiver: EBMessageSenderAddress - payload: { - status: StatusCode - message: string - data?: unknown - } - } & EBMessageBase + { + messageType: EBMessageType.CommandErrorResponse + contentType: 'application/json' + contentEncoding: 'utf-8' + isHandledError: boolean + correlationId: CorrelationId + receiver: EBMessageSenderAddress + payload: { + status: StatusCode + message: string + data?: unknown + } + } & EBMessageBase > diff --git a/packages/core/src/core/types/commandType/CommandFunction.ts b/packages/core/src/core/types/commandType/CommandFunction.ts index 2d8e2ffd8..5907dc647 100644 --- a/packages/core/src/core/types/commandType/CommandFunction.ts +++ b/packages/core/src/core/types/commandType/CommandFunction.ts @@ -1,3 +1,6 @@ +import type { Schema } from '@typeschema/main' +import type { EmptyObject } from '../EmptyObject.js' +import type { InvokeList } from '../InvokeList.js' import type { ServiceClass } from '../ServiceClass.js' import type { CommandFunctionContext } from './CommandFunctionContext.js' @@ -7,21 +10,22 @@ import type { CommandFunctionContext } from './CommandFunctionContext.js' * @group Command */ export type CommandFunction< - ServiceClassType extends ServiceClass, - MessagePayloadType = unknown, - MessageParamsType = unknown, - FunctionPayloadType = MessagePayloadType, - FunctionParamsType = MessageParamsType, - FunctionResultType = unknown, - Invokes = {}, - EmitListType = {}, + S extends ServiceClass, + MessagePayloadType = unknown, + MessageParamsType = unknown, + FunctionPayloadType = unknown, + FunctionParamsType = unknown, + FunctionOutputType = unknown, + Resources extends Record = EmptyObject, + Invokes extends InvokeList = EmptyObject, + EmitList extends Record = EmptyObject, > = ( - /** the service class */ - this: ServiceClassType, - /** the command function contest */ - context: CommandFunctionContext, - /** the transformed and validated payload */ - payload: Readonly, - /** the transformed and validated parameter object */ - parameter: Readonly, -) => Promise + /** the service class */ + this: S, + /** the command function contest */ + context: CommandFunctionContext, + /** the transformed and validated payload */ + payload: Readonly, + /** the transformed and validated parameter object */ + parameter: Readonly, +) => Promise diff --git a/packages/core/src/core/types/commandType/CommandFunctionContext.ts b/packages/core/src/core/types/commandType/CommandFunctionContext.ts index 4499b8e19..5c596c3ba 100644 --- a/packages/core/src/core/types/commandType/CommandFunctionContext.ts +++ b/packages/core/src/core/types/commandType/CommandFunctionContext.ts @@ -1,7 +1,8 @@ +import type { Schema } from '@typeschema/main' import type { ContextBase } from '../ContextBase.js' import type { EmitCustomMessageFunction } from '../EmitCustomMessageFunction.js' -import type { InvokeFunction } from '../InvokeFunction.js' import type { Prettify } from '../Prettify.js' +import type { EmptyObject, InvokeList } from '../index.js' import type { Command } from './Command.js' /** @@ -14,54 +15,36 @@ import type { Command } from './Command.js' * @group Command */ export type CommandFunctionContextEnhancements< - MessagePayloadType = unknown, - MessageParamsType = unknown, - Invokes = {}, - EmitListType = {}, + MessagePayloadType = unknown, + MessageParamsType = unknown, + Resources extends Record = EmptyObject, + Invokes extends InvokeList = EmptyObject, + EmitList extends Record = EmptyObject, > = { - /** the original message */ - message: Readonly> - /** emit a custom message */ - emit: EmitCustomMessageFunction - /** - * @deprecated Please use service instead and define the invocations with canInvoke in command builder. - * - * - * Invokes a command and returns the result. - * It is recommended to validate the result against a schema which only contains the data you actually need. - * - * @example - * ```typescript - * - * const address: EBMessageAddress = { - * serviceName: 'name-of-service-to-invoke', - * serviceVersion: '1', - * serviceTarget: 'command-name-to-invoke', - * } - * - * const inputPayload = { my: 'input' } - * const inputParameter = { search: 'for_me' } - * - * const result = await invoke(address, inputPayload inputParameter ) - * ``` - */ - invoke: InvokeFunction - /** - * Invokes a command and returns the result. - * It is recommended to validate the result against a schema which only contains the data you actually need. - * - * @example - * ```typescript - * // define your invocation in command builder - * .canInvoke('ServiceA', '1', 'test', responseOutputSchema, payloadSchema, parameterSchema) - * .setCommandFunction(async function (context, payload, _parameter) { - * const inputPayload = { my: 'input' } - * const inputParameter = { search: 'for_me' } - * const result = await context.service.ServiceA[1].test(inputPayload,inputParameter) - * }) - * ``` - */ - service: Invokes + /** the original message */ + message: Readonly> + /** emit a custom message */ + emit: EmitCustomMessageFunction + /** + * Invokes a command and returns the result. + * It is recommended to validate the result against a schema which only contains the data you actually need. + * + * @example + * ```typescript + * // define your invocation in command builder + * .canInvoke('ServiceA', '1', 'test', responseOutputSchema, payloadSchema, parameterSchema) + * .setCommandFunction(async function (context, payload, _parameter) { + * const inputPayload = { my: 'input' } + * const inputParameter = { search: 'for_me' } + * const result = await context.service.ServiceA[1].test(inputPayload,inputParameter) + * }) + * ``` + */ + service: Invokes + /** + * Provides resources defined in service builder and set via config during service creation + */ + resources: Resources } /** @@ -70,10 +53,11 @@ export type CommandFunctionContextEnhancements< * @group Command */ export type CommandFunctionContext< - MessagePayloadType = unknown, - MessageParamsType = unknown, - Invokes = {}, - EmitListType = {}, + MessagePayloadType = unknown, + MessageParamsType = unknown, + Resources extends Record = EmptyObject, + Invokes extends InvokeList = EmptyObject, + EmitList extends Record = EmptyObject, > = Prettify< - ContextBase & CommandFunctionContextEnhancements + ContextBase & CommandFunctionContextEnhancements > diff --git a/packages/core/src/core/types/commandType/CommandSuccessResponse.ts b/packages/core/src/core/types/commandType/CommandSuccessResponse.ts index 4fabc0f4a..f09342ea2 100644 --- a/packages/core/src/core/types/commandType/CommandSuccessResponse.ts +++ b/packages/core/src/core/types/commandType/CommandSuccessResponse.ts @@ -11,10 +11,10 @@ import type { Prettify } from '../Prettify.js' * @group Command */ export type CommandSuccessResponse = Prettify< - { - messageType: EBMessageType.CommandSuccessResponse - correlationId: CorrelationId - receiver: EBMessageSenderAddress - payload: PayloadType // result payload - } & EBMessageBase + { + messageType: EBMessageType.CommandSuccessResponse + correlationId: CorrelationId + receiver: EBMessageSenderAddress + payload: PayloadType // result payload + } & EBMessageBase > diff --git a/packages/core/src/core/types/commandType/CommandTransformFunctionContext.ts b/packages/core/src/core/types/commandType/CommandTransformFunctionContext.ts index 38f54958b..4a64473ae 100644 --- a/packages/core/src/core/types/commandType/CommandTransformFunctionContext.ts +++ b/packages/core/src/core/types/commandType/CommandTransformFunctionContext.ts @@ -5,9 +5,10 @@ import type { Command } from './Command.js' /** * @group Command */ -export type CommandTransformFunctionContext = Prettify< - ContextBase & { - /** the original message */ - message: Readonly> - } +export type CommandTransformFunctionContext = Prettify< + ContextBase & { + /** the original message */ + message: Readonly> + resources: Resources + } > diff --git a/packages/core/src/core/types/commandType/CommandTransformInputHook.ts b/packages/core/src/core/types/commandType/CommandTransformInputHook.ts index c4c5ee836..1a07736d2 100644 --- a/packages/core/src/core/types/commandType/CommandTransformInputHook.ts +++ b/packages/core/src/core/types/commandType/CommandTransformInputHook.ts @@ -1,3 +1,4 @@ +import type { EmptyObject, Service } from '../../index.js' import type { CommandTransformFunctionContext } from './CommandTransformFunctionContext.js' /** @@ -10,21 +11,24 @@ import type { CommandTransformFunctionContext } from './CommandTransformFunction * @group Command */ export type CommandTransformInputHook< - ServiceClassType, - FunctionPayloadOutput = unknown, - FunctionParamsOutput = unknown, - MessagePayloadInput = unknown, - MessageParamsInput = unknown, + S extends Service, + MessagePayloadType, + MessageParamsType, + TransformInputPayload, + TransformInputParams, + FunctionPayloadType, + FunctionParamsType, + Resources extends Record = EmptyObject, > = ( - /** the service instance */ - this: ServiceClassType, - /** the transform function context */ - context: CommandTransformFunctionContext, - /** the payload validated against the transform payload schema */ - payload: Readonly, - /** the payload validated against the transform parameter schema */ - parameter: Readonly, + /** the service instance */ + this: S, + /** the transform function context */ + context: CommandTransformFunctionContext, + /** the payload validated against the transform payload schema */ + payload: Readonly, + /** the payload validated against the transform parameter schema */ + parameter: Readonly, ) => Promise<{ - payload: Readonly - parameter: Readonly + payload: FunctionPayloadType + parameter: FunctionParamsType }> diff --git a/packages/core/src/core/types/commandType/CommandTransformOutputHook.ts b/packages/core/src/core/types/commandType/CommandTransformOutputHook.ts index 076f0a2d9..b63ce4d67 100644 --- a/packages/core/src/core/types/commandType/CommandTransformOutputHook.ts +++ b/packages/core/src/core/types/commandType/CommandTransformOutputHook.ts @@ -1,3 +1,4 @@ +import type { EmptyObject, Service } from '../../index.js' import type { CommandTransformFunctionContext } from './CommandTransformFunctionContext.js' /** @@ -6,20 +7,21 @@ import type { CommandTransformFunctionContext } from './CommandTransformFunction * @group Command * * @param context the Context - * @param commandFunctionOutput The output result output of command function - * @param commandFunctionInputParameter The parameter input given to command function + * @param input The output result output of command function + * @param params The parameter input given to command function * @returns The transformed message payload */ export type CommandTransformOutputHook< - ServiceClassType, - MessagePayloadType, - MessageParamsType, - MessageResultType, - FunctionResultType, - FunctionParamsType, + S extends Service, + MessagePayloadType, + MessageParamsType, + FinalFunctionOutputType, + FunctionParamsType, + TransformOutputHookOutput, + Resources extends Record = EmptyObject, > = ( - this: ServiceClassType, - context: CommandTransformFunctionContext, - commandFunctionOutput: Readonly, - commandFunctionInputParameter: Readonly, -) => Promise + this: S, + context: CommandTransformFunctionContext, + input: Readonly, + params: Readonly, +) => Promise diff --git a/packages/core/src/core/types/commandType/isCommand.impl.ts b/packages/core/src/core/types/commandType/isCommand.impl.ts index 5b8bd8852..d8eded093 100644 --- a/packages/core/src/core/types/commandType/isCommand.impl.ts +++ b/packages/core/src/core/types/commandType/isCommand.impl.ts @@ -10,5 +10,5 @@ import type { Command } from './Command.js' * @returns boolean */ export const isCommand = (message: EBMessage): message is Command => { - return message.messageType === EBMessageType.Command + return message.messageType === EBMessageType.Command } diff --git a/packages/core/src/core/types/commandType/isCommand.test.ts b/packages/core/src/core/types/commandType/isCommand.test.ts index 5753b0648..ec7205a9c 100644 --- a/packages/core/src/core/types/commandType/isCommand.test.ts +++ b/packages/core/src/core/types/commandType/isCommand.test.ts @@ -2,13 +2,13 @@ import { getCommandMessageMock, getCommandSuccessMessageMock } from '../../../mo import { isCommand } from './isCommand.impl.js' describe('isCommand', () => { - it('returns true if it is a command message', () => { - const message = getCommandMessageMock() - expect(isCommand(message)).toBeTruthy() - }) + it('returns true if it is a command message', () => { + const message = getCommandMessageMock() + expect(isCommand(message)).toBeTruthy() + }) - it('returns false if it is not a command message', () => { - const message = getCommandSuccessMessageMock({}) - expect(isCommand(message)).toBeFalsy() - }) + it('returns false if it is not a command message', () => { + const message = getCommandSuccessMessageMock({}) + expect(isCommand(message)).toBeFalsy() + }) }) diff --git a/packages/core/src/core/types/commandType/isCommandErrorResponse.impl.ts b/packages/core/src/core/types/commandType/isCommandErrorResponse.impl.ts index 606a4f3d3..a7d39c486 100644 --- a/packages/core/src/core/types/commandType/isCommandErrorResponse.impl.ts +++ b/packages/core/src/core/types/commandType/isCommandErrorResponse.impl.ts @@ -10,9 +10,9 @@ import type { CommandErrorResponse } from './CommandErrorResponse.js' * @returns boolean */ export const isCommandErrorResponse = (message: EBMessage | unknown): message is CommandErrorResponse => { - if (typeof message !== 'object' || message === null) { - return false - } - const m = message as CommandErrorResponse - return m.messageType === EBMessageType.CommandErrorResponse + if (typeof message !== 'object' || message === null) { + return false + } + const m = message as CommandErrorResponse + return m.messageType === EBMessageType.CommandErrorResponse } diff --git a/packages/core/src/core/types/commandType/isCommandErrorResponse.test.ts b/packages/core/src/core/types/commandType/isCommandErrorResponse.test.ts index 38748fe05..ebdf27a07 100644 --- a/packages/core/src/core/types/commandType/isCommandErrorResponse.test.ts +++ b/packages/core/src/core/types/commandType/isCommandErrorResponse.test.ts @@ -2,21 +2,21 @@ import { getCommandErrorMessageMock, getCommandMessageMock } from '../../../mock import { isCommandErrorResponse } from './isCommandErrorResponse.impl.js' describe('isCommandErrorResponse', () => { - it('returns true if it is a command error message', () => { - const message = getCommandErrorMessageMock() - expect(isCommandErrorResponse(message)).toBeTruthy() - }) + it('returns true if it is a command error message', () => { + const message = getCommandErrorMessageMock() + expect(isCommandErrorResponse(message)).toBeTruthy() + }) - it('returns false if it is not a command error message', () => { - const message = getCommandMessageMock() - expect(isCommandErrorResponse(message)).toBeFalsy() - }) + it('returns false if it is not a command error message', () => { + const message = getCommandMessageMock() + expect(isCommandErrorResponse(message)).toBeFalsy() + }) - it('returns false if it is not a message', () => { - expect(isCommandErrorResponse('string')).toBeFalsy() - }) + it('returns false if it is not a message', () => { + expect(isCommandErrorResponse('string')).toBeFalsy() + }) - it('returns false if it gets null', () => { - expect(isCommandErrorResponse(null)).toBeFalsy() - }) + it('returns false if it gets null', () => { + expect(isCommandErrorResponse(null)).toBeFalsy() + }) }) diff --git a/packages/core/src/core/types/commandType/isCommandResponse.impl.ts b/packages/core/src/core/types/commandType/isCommandResponse.impl.ts index 9be03d9a9..5820e469a 100644 --- a/packages/core/src/core/types/commandType/isCommandResponse.impl.ts +++ b/packages/core/src/core/types/commandType/isCommandResponse.impl.ts @@ -10,8 +10,8 @@ import type { CommandResponse } from './CommandResponse.js' * @returns boolean */ export const isCommandResponse = (message: EBMessage): message is CommandResponse => { - return ( - message.messageType === EBMessageType.CommandSuccessResponse || - message.messageType === EBMessageType.CommandErrorResponse - ) + return ( + message.messageType === EBMessageType.CommandSuccessResponse || + message.messageType === EBMessageType.CommandErrorResponse + ) } diff --git a/packages/core/src/core/types/commandType/isCommandResponse.test.ts b/packages/core/src/core/types/commandType/isCommandResponse.test.ts index 878f5a5bc..56248267d 100644 --- a/packages/core/src/core/types/commandType/isCommandResponse.test.ts +++ b/packages/core/src/core/types/commandType/isCommandResponse.test.ts @@ -1,23 +1,23 @@ import { - getCommandErrorMessageMock, - getCommandMessageMock, - getCommandSuccessMessageMock, + getCommandErrorMessageMock, + getCommandMessageMock, + getCommandSuccessMessageMock, } from '../../../mocks/index.js' import { isCommandResponse } from './isCommandResponse.impl.js' describe('isCommandResponse', () => { - it('returns true if it is a command error response message', () => { - const message = getCommandErrorMessageMock() - expect(isCommandResponse(message)).toBeTruthy() - }) + it('returns true if it is a command error response message', () => { + const message = getCommandErrorMessageMock() + expect(isCommandResponse(message)).toBeTruthy() + }) - it('returns true if it is a command success response message', () => { - const message = getCommandSuccessMessageMock({}) - expect(isCommandResponse(message)).toBeTruthy() - }) + it('returns true if it is a command success response message', () => { + const message = getCommandSuccessMessageMock({}) + expect(isCommandResponse(message)).toBeTruthy() + }) - it('returns false if it is not a command response message', () => { - const message = getCommandMessageMock() - expect(isCommandResponse(message)).toBeFalsy() - }) + it('returns false if it is not a command response message', () => { + const message = getCommandMessageMock() + expect(isCommandResponse(message)).toBeFalsy() + }) }) diff --git a/packages/core/src/core/types/commandType/isCommandSuccessResponse.impl.ts b/packages/core/src/core/types/commandType/isCommandSuccessResponse.impl.ts index 4598833df..c8c3545af 100644 --- a/packages/core/src/core/types/commandType/isCommandSuccessResponse.impl.ts +++ b/packages/core/src/core/types/commandType/isCommandSuccessResponse.impl.ts @@ -10,5 +10,5 @@ import type { CommandSuccessResponse } from './CommandSuccessResponse.js' * @returns boolean */ export const isCommandSuccessResponse = (message: EBMessage): message is CommandSuccessResponse => { - return message.messageType === EBMessageType.CommandSuccessResponse + return message.messageType === EBMessageType.CommandSuccessResponse } diff --git a/packages/core/src/core/types/commandType/isCommandSuccessResponse.test.ts b/packages/core/src/core/types/commandType/isCommandSuccessResponse.test.ts index 819b7ca8d..9f23e51ce 100644 --- a/packages/core/src/core/types/commandType/isCommandSuccessResponse.test.ts +++ b/packages/core/src/core/types/commandType/isCommandSuccessResponse.test.ts @@ -2,13 +2,13 @@ import { getCommandErrorMessageMock, getCommandSuccessMessageMock } from '../../ import { isCommandSuccessResponse } from './isCommandSuccessResponse.impl.js' describe('isCommandSuccessResponse', () => { - it('returns true if it is a command success message', () => { - const message = getCommandSuccessMessageMock({}) - expect(isCommandSuccessResponse(message)).toBeTruthy() - }) + it('returns true if it is a command success message', () => { + const message = getCommandSuccessMessageMock({}) + expect(isCommandSuccessResponse(message)).toBeTruthy() + }) - it('returns false if it is not a command success message', () => { - const message = getCommandErrorMessageMock() - expect(isCommandSuccessResponse(message)).toBeFalsy() - }) + it('returns false if it is not a command success message', () => { + const message = getCommandErrorMessageMock() + expect(isCommandSuccessResponse(message)).toBeFalsy() + }) }) diff --git a/packages/core/src/core/types/index.ts b/packages/core/src/core/types/index.ts index 059c8a6a1..c308ee9b2 100644 --- a/packages/core/src/core/types/index.ts +++ b/packages/core/src/core/types/index.ts @@ -23,7 +23,6 @@ export * from './FromInvokeToOtherType.js' export * from './GenericEventEmitter.js' export * from './infoType/index.js' export * from './InstanceId.js' -export * from './InvokeFunction.js' export * from './isCustomMessage.impl.js' export * from './Logger.js' export * from './LogLevelName.js' @@ -40,3 +39,13 @@ export * from './StoreType.enum.js' export * from './subscription/index.js' export * from './TenantId.js' export * from './TraceId.js' +export * from './EmptyObject.js' +export * from './ServiceClassTypes.js' +export * from './ServiceBuilderTypes.js' +export * from './NeverObject.js' +export * from './SetNewTypeValue.js' +export * from './InferTypeOrEmptyObject.js' +export * from './InvokeFunction.js' +export * from './InvokeList.js' +export * from './GetMessagePayloadType.js' +export * from './GetMessageParamsType.js' diff --git a/packages/core/src/core/types/infoType/InfoInvokeTimeout.ts b/packages/core/src/core/types/infoType/InfoInvokeTimeout.ts index 7b5193aba..d29f9d61d 100644 --- a/packages/core/src/core/types/infoType/InfoInvokeTimeout.ts +++ b/packages/core/src/core/types/infoType/InfoInvokeTimeout.ts @@ -4,21 +4,21 @@ import type { TraceId } from '../TraceId.js' import type { InfoServiceBase } from './InfoServiceBase.js' export type InfoInvokeTimeoutPayload = { - traceId: TraceId - correlationId: CorrelationId - sender: { - serviceName: string - serviceVersion: string - serviceTarget: string - } - receiver: { - serviceName: string - serviceVersion: string - serviceTarget: string - } - timestamp: number + traceId: TraceId + correlationId: CorrelationId + sender: { + serviceName: string + serviceVersion: string + serviceTarget: string + } + receiver: { + serviceName: string + serviceVersion: string + serviceTarget: string + } + timestamp: number } export type InfoInvokeTimeout = { - messageType: EBMessageType.InfoInvokeTimeout + messageType: EBMessageType.InfoInvokeTimeout } & InfoServiceBase diff --git a/packages/core/src/core/types/infoType/InfoMessage.ts b/packages/core/src/core/types/infoType/InfoMessage.ts index b539868fa..102f9e39a 100644 --- a/packages/core/src/core/types/infoType/InfoMessage.ts +++ b/packages/core/src/core/types/infoType/InfoMessage.ts @@ -9,31 +9,31 @@ import type { InfoServiceShutdown } from './InfoServiceShutdown.js' import type { InfoSubscriptionError } from './InfoSubscriptionError.js' export type InfoMessage = - | InfoServiceDrain - | InfoServiceFunctionAdded - | InfoServiceInit - | InfoServiceNotReady - | InfoServiceReady - | InfoServiceShutdown - | InfoInvokeTimeout - | InfoSubscriptionError + | InfoServiceDrain + | InfoServiceFunctionAdded + | InfoServiceInit + | InfoServiceNotReady + | InfoServiceReady + | InfoServiceShutdown + | InfoInvokeTimeout + | InfoSubscriptionError export type InfoMessageType = - | EBMessageType.InfoServiceDrain - | EBMessageType.InfoServiceFunctionAdded - | EBMessageType.InfoServiceInit - | EBMessageType.InfoServiceNotReady - | EBMessageType.InfoServiceReady - | EBMessageType.InfoServiceShutdown - | EBMessageType.InfoInvokeTimeout - | EBMessageType.InfoSubscriptionError + | EBMessageType.InfoServiceDrain + | EBMessageType.InfoServiceFunctionAdded + | EBMessageType.InfoServiceInit + | EBMessageType.InfoServiceNotReady + | EBMessageType.InfoServiceReady + | EBMessageType.InfoServiceShutdown + | EBMessageType.InfoInvokeTimeout + | EBMessageType.InfoSubscriptionError export const infoMessageTypes = [ - EBMessageType.InfoServiceDrain, - EBMessageType.InfoServiceFunctionAdded, - EBMessageType.InfoServiceInit, - EBMessageType.InfoServiceNotReady, - EBMessageType.InfoServiceReady, - EBMessageType.InfoServiceShutdown, - EBMessageType.InfoInvokeTimeout, - EBMessageType.InfoSubscriptionError, + EBMessageType.InfoServiceDrain, + EBMessageType.InfoServiceFunctionAdded, + EBMessageType.InfoServiceInit, + EBMessageType.InfoServiceNotReady, + EBMessageType.InfoServiceReady, + EBMessageType.InfoServiceShutdown, + EBMessageType.InfoInvokeTimeout, + EBMessageType.InfoSubscriptionError, ] diff --git a/packages/core/src/core/types/infoType/InfoServiceBase.ts b/packages/core/src/core/types/infoType/InfoServiceBase.ts index 379867cc3..17f46bc8f 100644 --- a/packages/core/src/core/types/infoType/InfoServiceBase.ts +++ b/packages/core/src/core/types/infoType/InfoServiceBase.ts @@ -2,9 +2,9 @@ import type { EBMessageBase } from '../EBMessageBase.js' import type { Prettify } from '../Prettify.js' export type InfoServiceBase = Prettify< - { - contentType: 'application/json' - contentEncoding: 'utf-8' - payload?: unknown - } & EBMessageBase + { + contentType: 'application/json' + contentEncoding: 'utf-8' + payload?: unknown + } & EBMessageBase > diff --git a/packages/core/src/core/types/infoType/InfoServiceDrain.ts b/packages/core/src/core/types/infoType/InfoServiceDrain.ts index 1f5a36938..1ec8f0dc6 100644 --- a/packages/core/src/core/types/infoType/InfoServiceDrain.ts +++ b/packages/core/src/core/types/infoType/InfoServiceDrain.ts @@ -3,7 +3,7 @@ import type { Prettify } from '../Prettify.js' import type { InfoServiceBase } from './InfoServiceBase.js' export type InfoServiceDrain = Prettify< - { - messageType: EBMessageType.InfoServiceDrain - } & InfoServiceBase + { + messageType: EBMessageType.InfoServiceDrain + } & InfoServiceBase > diff --git a/packages/core/src/core/types/infoType/InfoServiceFunctionAdded.ts b/packages/core/src/core/types/infoType/InfoServiceFunctionAdded.ts index d1c76f87f..4a29217aa 100644 --- a/packages/core/src/core/types/infoType/InfoServiceFunctionAdded.ts +++ b/packages/core/src/core/types/infoType/InfoServiceFunctionAdded.ts @@ -3,7 +3,7 @@ import type { Prettify } from '../Prettify.js' import type { InfoServiceBase } from './InfoServiceBase.js' export type InfoServiceFunctionAdded = Prettify< - { - messageType: EBMessageType.InfoServiceFunctionAdded - } & InfoServiceBase + { + messageType: EBMessageType.InfoServiceFunctionAdded + } & InfoServiceBase > diff --git a/packages/core/src/core/types/infoType/InfoServiceInit.ts b/packages/core/src/core/types/infoType/InfoServiceInit.ts index 98579c338..38d6e9c43 100644 --- a/packages/core/src/core/types/infoType/InfoServiceInit.ts +++ b/packages/core/src/core/types/infoType/InfoServiceInit.ts @@ -3,7 +3,7 @@ import type { Prettify } from '../Prettify.js' import type { InfoServiceBase } from './InfoServiceBase.js' export type InfoServiceInit = Prettify< - { - messageType: EBMessageType.InfoServiceInit - } & InfoServiceBase + { + messageType: EBMessageType.InfoServiceInit + } & InfoServiceBase > diff --git a/packages/core/src/core/types/infoType/InfoServiceNotReady.ts b/packages/core/src/core/types/infoType/InfoServiceNotReady.ts index 412bf7590..eb495104c 100644 --- a/packages/core/src/core/types/infoType/InfoServiceNotReady.ts +++ b/packages/core/src/core/types/infoType/InfoServiceNotReady.ts @@ -3,7 +3,7 @@ import type { Prettify } from '../Prettify.js' import type { InfoServiceBase } from './InfoServiceBase.js' export type InfoServiceNotReady = Prettify< - { - messageType: EBMessageType.InfoServiceNotReady - } & InfoServiceBase + { + messageType: EBMessageType.InfoServiceNotReady + } & InfoServiceBase > diff --git a/packages/core/src/core/types/infoType/InfoServiceReady.ts b/packages/core/src/core/types/infoType/InfoServiceReady.ts index d61155de0..86d599d7c 100644 --- a/packages/core/src/core/types/infoType/InfoServiceReady.ts +++ b/packages/core/src/core/types/infoType/InfoServiceReady.ts @@ -3,7 +3,7 @@ import type { Prettify } from '../Prettify.js' import type { InfoServiceBase } from './InfoServiceBase.js' export type InfoServiceReady = Prettify< - { - messageType: EBMessageType.InfoServiceReady - } & InfoServiceBase + { + messageType: EBMessageType.InfoServiceReady + } & InfoServiceBase > diff --git a/packages/core/src/core/types/infoType/InfoServiceShutdown.ts b/packages/core/src/core/types/infoType/InfoServiceShutdown.ts index a9445a154..5853260c5 100644 --- a/packages/core/src/core/types/infoType/InfoServiceShutdown.ts +++ b/packages/core/src/core/types/infoType/InfoServiceShutdown.ts @@ -3,7 +3,7 @@ import type { Prettify } from '../Prettify.js' import type { InfoServiceBase } from './InfoServiceBase.js' export type InfoServiceShutdown = Prettify< - { - messageType: EBMessageType.InfoServiceShutdown - } & InfoServiceBase + { + messageType: EBMessageType.InfoServiceShutdown + } & InfoServiceBase > diff --git a/packages/core/src/core/types/infoType/InfoSubscriptionError.ts b/packages/core/src/core/types/infoType/InfoSubscriptionError.ts index 82bb38a28..6be670baf 100644 --- a/packages/core/src/core/types/infoType/InfoSubscriptionError.ts +++ b/packages/core/src/core/types/infoType/InfoSubscriptionError.ts @@ -3,7 +3,7 @@ import type { Prettify } from '../Prettify.js' import type { InfoServiceBase } from './InfoServiceBase.js' export type InfoSubscriptionError = Prettify< - { - messageType: EBMessageType.InfoSubscriptionError - } & InfoServiceBase + { + messageType: EBMessageType.InfoSubscriptionError + } & InfoServiceBase > diff --git a/packages/core/src/core/types/infoType/ServiceInfoType.ts b/packages/core/src/core/types/infoType/ServiceInfoType.ts index f8786345a..15d66e118 100644 --- a/packages/core/src/core/types/infoType/ServiceInfoType.ts +++ b/packages/core/src/core/types/infoType/ServiceInfoType.ts @@ -2,7 +2,7 @@ * General service information */ export type ServiceInfoType = { - serviceName: Exclude - serviceVersion: Exclude - serviceDescription: string + serviceName: Exclude + serviceVersion: Exclude + serviceDescription: string } diff --git a/packages/core/src/core/types/infoType/isInfoMessage.impl.ts b/packages/core/src/core/types/infoType/isInfoMessage.impl.ts index b6ce7e2ea..423d7fe55 100644 --- a/packages/core/src/core/types/infoType/isInfoMessage.impl.ts +++ b/packages/core/src/core/types/infoType/isInfoMessage.impl.ts @@ -3,5 +3,5 @@ import type { InfoMessage } from './InfoMessage.js' import { infoMessageTypes } from './InfoMessage.js' export const isInfoMessage = (message: EBMessage): message is InfoMessage => { - return infoMessageTypes.includes(message.messageType) + return infoMessageTypes.includes(message.messageType) } diff --git a/packages/core/src/core/types/infoType/isInfoMessage.test.ts b/packages/core/src/core/types/infoType/isInfoMessage.test.ts index 1d6adcefc..f1f677b68 100644 --- a/packages/core/src/core/types/infoType/isInfoMessage.test.ts +++ b/packages/core/src/core/types/infoType/isInfoMessage.test.ts @@ -5,18 +5,18 @@ import { EBMessageType } from '../EBMessageType.enum.js' import { isInfoMessage } from './isInfoMessage.impl.js' describe('isInfoMessage', () => { - it('returns true if it is a info message', () => { - const message = createInfoMessage(EBMessageType.InfoServiceFunctionAdded, { - serviceName: 'serviceName', - serviceVersion: '1', - serviceTarget: '', - instanceId: 'a', - }) - expect(isInfoMessage(message as EBMessage)).toBeTruthy() - }) + it('returns true if it is a info message', () => { + const message = createInfoMessage(EBMessageType.InfoServiceFunctionAdded, { + serviceName: 'serviceName', + serviceVersion: '1', + serviceTarget: '', + instanceId: 'a', + }) + expect(isInfoMessage(message as EBMessage)).toBeTruthy() + }) - it('returns false if it is not a info message', () => { - const message = getCustomMessageMessageMock('123', {}) - expect(isInfoMessage(message as EBMessage)).toBeFalsy() - }) + it('returns false if it is not a info message', () => { + const message = getCustomMessageMessageMock('123', {}) + expect(isInfoMessage(message as EBMessage)).toBeFalsy() + }) }) diff --git a/packages/core/src/core/types/infoType/isInfoServiceFunctionAdded.impl.ts b/packages/core/src/core/types/infoType/isInfoServiceFunctionAdded.impl.ts index ab5d14ea1..39c18a1c7 100644 --- a/packages/core/src/core/types/infoType/isInfoServiceFunctionAdded.impl.ts +++ b/packages/core/src/core/types/infoType/isInfoServiceFunctionAdded.impl.ts @@ -3,5 +3,5 @@ import { EBMessageType } from '../EBMessageType.enum.js' import type { InfoServiceFunctionAdded } from './InfoServiceFunctionAdded.js' export const isInfoServiceFunctionAdded = (message: EBMessage): message is InfoServiceFunctionAdded => { - return message.messageType === EBMessageType.InfoServiceFunctionAdded + return message.messageType === EBMessageType.InfoServiceFunctionAdded } diff --git a/packages/core/src/core/types/infoType/isInfoServiceFunctionAdded.test.ts b/packages/core/src/core/types/infoType/isInfoServiceFunctionAdded.test.ts index d56e3df0e..60d661915 100644 --- a/packages/core/src/core/types/infoType/isInfoServiceFunctionAdded.test.ts +++ b/packages/core/src/core/types/infoType/isInfoServiceFunctionAdded.test.ts @@ -4,23 +4,23 @@ import { EBMessageType } from '../EBMessageType.enum.js' import { isInfoServiceFunctionAdded } from './isInfoServiceFunctionAdded.impl.js' describe('isInfoServiceFunctionAdded', () => { - it('returns true if it is a info service added message', () => { - const message = createInfoMessage(EBMessageType.InfoServiceFunctionAdded, { - serviceName: 'serviceName', - serviceVersion: '1', - serviceTarget: '', - instanceId: 'a', - }) - expect(isInfoServiceFunctionAdded(message as EBMessage)).toBeTruthy() - }) + it('returns true if it is a info service added message', () => { + const message = createInfoMessage(EBMessageType.InfoServiceFunctionAdded, { + serviceName: 'serviceName', + serviceVersion: '1', + serviceTarget: '', + instanceId: 'a', + }) + expect(isInfoServiceFunctionAdded(message as EBMessage)).toBeTruthy() + }) - it('returns false if it is not a info service added message', () => { - const message = createInfoMessage(EBMessageType.InfoServiceInit, { - serviceName: 'serviceName', - serviceVersion: '1', - serviceTarget: '', - instanceId: 'a', - }) - expect(isInfoServiceFunctionAdded(message as EBMessage)).toBeFalsy() - }) + it('returns false if it is not a info service added message', () => { + const message = createInfoMessage(EBMessageType.InfoServiceInit, { + serviceName: 'serviceName', + serviceVersion: '1', + serviceTarget: '', + instanceId: 'a', + }) + expect(isInfoServiceFunctionAdded(message as EBMessage)).toBeFalsy() + }) }) diff --git a/packages/core/src/core/types/isCustomMessage.impl.ts b/packages/core/src/core/types/isCustomMessage.impl.ts index 0c97cb7bd..033c88954 100644 --- a/packages/core/src/core/types/isCustomMessage.impl.ts +++ b/packages/core/src/core/types/isCustomMessage.impl.ts @@ -8,5 +8,5 @@ import { EBMessageType } from './EBMessageType.enum.js' * @returns true if message is type of custom message */ export const isCustomMessage = (message: EBMessage): message is CustomMessage => { - return message.messageType === EBMessageType.CustomMessage + return message.messageType === EBMessageType.CustomMessage } diff --git a/packages/core/src/core/types/isCustomMessage.test.ts b/packages/core/src/core/types/isCustomMessage.test.ts index e2ad441e5..1fef5a66b 100644 --- a/packages/core/src/core/types/isCustomMessage.test.ts +++ b/packages/core/src/core/types/isCustomMessage.test.ts @@ -2,13 +2,13 @@ import { getCommandSuccessMessageMock, getCustomMessageMessageMock } from '../.. import { isCustomMessage } from './isCustomMessage.impl.js' describe('isCustomMessage', () => { - it('returns true if it is a custom message', () => { - const message = getCustomMessageMessageMock('test', {}) - expect(isCustomMessage(message)).toBeTruthy() - }) + it('returns true if it is a custom message', () => { + const message = getCustomMessageMessageMock('test', {}) + expect(isCustomMessage(message)).toBeTruthy() + }) - it('returns false if it is not a custom message', () => { - const message = getCommandSuccessMessageMock({}) - expect(isCustomMessage(message)).toBeFalsy() - }) + it('returns false if it is not a custom message', () => { + const message = getCommandSuccessMessageMock({}) + expect(isCustomMessage(message)).toBeFalsy() + }) }) diff --git a/packages/core/src/core/types/subscription/Subscription.ts b/packages/core/src/core/types/subscription/Subscription.ts index a87e26acb..34787abbf 100644 --- a/packages/core/src/core/types/subscription/Subscription.ts +++ b/packages/core/src/core/types/subscription/Subscription.ts @@ -11,37 +11,37 @@ import type { TenantId } from '../TenantId.js' * @group Subscription */ export type Subscription = { - /** the producer address of the message */ - sender?: { - serviceName?: string - serviceVersion?: string - serviceTarget?: string - instanceId?: InstanceId - } - /** the consumer address of the message */ - receiver?: { - serviceName?: string - serviceVersion?: string - serviceTarget?: string - instanceId?: InstanceId - } - /** the message type */ - messageType?: EBMessageType - /** the event name to subscribe for */ - eventName?: string // event to listen for - /** the event name to be used for custom message if the subscriptions returns a result */ - emitEventName?: string // event to emit if output payload is set - /** the principal id */ - principalId?: PrincipalId - /** the tenant id */ - tenantId?: TenantId - /** the message payload */ - payload?: { - parameter?: ParameterType - payload?: PayloadType - } - /** the address of the subscription (service name, version and subscription name) */ - subscriber: EBMessageAddress - /** config information for event bridge */ - eventBridgeConfig: DefinitionEventBridgeConfig + /** the producer address of the message */ + sender?: { + serviceName?: string + serviceVersion?: string + serviceTarget?: string + instanceId?: InstanceId + } + /** the consumer address of the message */ + receiver?: { + serviceName?: string + serviceVersion?: string + serviceTarget?: string + instanceId?: InstanceId + } + /** the message type */ + messageType?: EBMessageType + /** the event name to subscribe for */ + eventName?: string // event to listen for + /** the event name to be used for custom message if the subscriptions returns a result */ + emitEventName?: string // event to emit if output payload is set + /** the principal id */ + principalId?: PrincipalId + /** the tenant id */ + tenantId?: TenantId + /** the message payload */ + payload?: { + parameter?: ParameterType + payload?: PayloadType + } + /** the address of the subscription (service name, version and subscription name) */ + subscriber: EBMessageAddress + /** config information for event bridge */ + eventBridgeConfig: DefinitionEventBridgeConfig } diff --git a/packages/core/src/core/types/subscription/SubscriptionAfterGuardHook.ts b/packages/core/src/core/types/subscription/SubscriptionAfterGuardHook.ts index ad96b33ce..ea80acfb0 100644 --- a/packages/core/src/core/types/subscription/SubscriptionAfterGuardHook.ts +++ b/packages/core/src/core/types/subscription/SubscriptionAfterGuardHook.ts @@ -1,3 +1,6 @@ +import type { Schema } from '@typeschema/main' +import type { EmptyObject } from '../EmptyObject.js' +import type { InvokeList } from '../InvokeList.js' import type { ServiceClass } from '../ServiceClass.js' import type { SubscriptionFunctionContext } from './SubscriptionFunctionContext.js' @@ -8,16 +11,17 @@ import type { SubscriptionFunctionContext } from './SubscriptionFunctionContext. * @group Subscription */ export type SubscriptionAfterGuardHook< - ServiceClassType = ServiceClass, - FunctionResultType = unknown, - FunctionPayloadOutputType = unknown, - FunctionParameterType = unknown, - Invokes = {}, - EmitListType = {}, + ServiceClassType = ServiceClass, + FunctionResultType = unknown, + FunctionPayloadOutputType = unknown, + FunctionParameterType = unknown, + Resources extends Record = EmptyObject, + Invokes extends InvokeList = EmptyObject, + EmitList extends Record = EmptyObject, > = ( - this: ServiceClassType, - context: SubscriptionFunctionContext, - result: Readonly, - payload: Readonly, - parameter: Readonly, + this: ServiceClassType, + context: SubscriptionFunctionContext, + result: Readonly, + payload: Readonly, + parameter: Readonly, ) => Promise diff --git a/packages/core/src/core/types/subscription/SubscriptionBeforeGuardHook.ts b/packages/core/src/core/types/subscription/SubscriptionBeforeGuardHook.ts index e2dd58258..76bd73b85 100644 --- a/packages/core/src/core/types/subscription/SubscriptionBeforeGuardHook.ts +++ b/packages/core/src/core/types/subscription/SubscriptionBeforeGuardHook.ts @@ -1,4 +1,7 @@ -import type { ServiceClass } from '../ServiceClass.js' +import type { Schema } from '@typeschema/main' +import type { Service } from '../../Service/index.js' +import type { EmptyObject } from '../EmptyObject.js' +import type { InvokeList } from '../InvokeList.js' import type { SubscriptionFunctionContext } from './SubscriptionFunctionContext.js' /** @@ -9,14 +12,15 @@ import type { SubscriptionFunctionContext } from './SubscriptionFunctionContext. * @group Subscription */ export type SubscriptionBeforeGuardHook< - ServiceClassType = ServiceClass, - FunctionPayloadType = unknown, - FunctionParamsType = unknown, - Invokes = {}, - EmitListType = {}, + S extends Service = Service, + FunctionPayloadType = unknown, + FunctionParamsType = unknown, + Resources extends Record = EmptyObject, + Invokes extends InvokeList = EmptyObject, + EmitList extends Record = EmptyObject, > = ( - this: ServiceClassType, - context: SubscriptionFunctionContext, - payload: Readonly, - parameter: Readonly, + this: S, + context: SubscriptionFunctionContext, + payload: Readonly, + parameter: Readonly, ) => Promise diff --git a/packages/core/src/core/types/subscription/SubscriptionDefinition.ts b/packages/core/src/core/types/subscription/SubscriptionDefinition.ts index 88fc09127..7b48d0b37 100644 --- a/packages/core/src/core/types/subscription/SubscriptionDefinition.ts +++ b/packages/core/src/core/types/subscription/SubscriptionDefinition.ts @@ -1,11 +1,14 @@ import type { Schema } from '@typeschema/main' +import type { SchemaObject } from 'openapi3-ts/oas31' +import type { Service } from '../../Service/index.js' import type { DefinitionEventBridgeConfig } from '../DefinitionEventBridgeConfig.js' import type { EBMessageType } from '../EBMessageType.enum.js' +import type { FromEmitToOtherType } from '../FromEmitToOtherType.js' import type { FromInvokeToOtherType } from '../FromInvokeToOtherType.js' import type { InstanceId } from '../InstanceId.js' +import type { InvokeList } from '../InvokeList.js' import type { PrincipalId } from '../PrincipalId.js' -import type { ServiceClass } from '../ServiceClass.js' import type { TenantId } from '../TenantId.js' import type { SubscriptionAfterGuardHook } from './SubscriptionAfterGuardHook.js' import type { SubscriptionBeforeGuardHook } from './SubscriptionBeforeGuardHook.js' @@ -20,87 +23,104 @@ import type { SubscriptionTransformOutputHook } from './SubscriptionTransformOut * @group Subscription */ export type SubscriptionDefinition< - ServiceClassType extends ServiceClass = ServiceClass, - MetadataType = SubscriptionDefinitionMetadataBase, - MessagePayloadType = unknown, - MessageParamsType = unknown, - MessageResultType = unknown, - FunctionPayloadType = MessagePayloadType, - FunctionParamsType = MessageParamsType, - FunctionResultType = MessageResultType, - Invokes = {}, - EmitListType = {}, + S extends Service, + TransformInputPayload, + TransformInputParams, + FunctionPayloadType, + FunctionParamsType, + FunctionOutputType, + FinalFunctionOutputType, + TransformOutputHookOutput, + Resources extends Record, + Invokes extends InvokeList, + EmitList extends Record, + MetadataType extends SubscriptionDefinitionMetadataBase = SubscriptionDefinitionMetadataBase, > = { - /** the name of the subscription */ - subscriptionName: string - /** the description of the subscription */ - subscriptionDescription: string - /** the metadata of the subscription */ - metadata: MetadataType - /** config information for event bridge */ - eventBridgeConfig: DefinitionEventBridgeConfig - /** the subscription function */ - call: SubscriptionFunction< - ServiceClassType, - MessagePayloadType, - MessageParamsType, - FunctionPayloadType, - FunctionParamsType, - FunctionResultType, - Invokes, - EmitListType - > - /** filter for messages produced by given sender */ - sender?: { - serviceName?: string - serviceVersion?: string - serviceTarget?: string - instanceId?: InstanceId - } - /** filter for messages consumed by given receiver */ - receiver?: { - serviceName?: string - serviceVersion?: string - serviceTarget?: string - instanceId?: InstanceId - } - /** filter for message type */ - messageType?: EBMessageType - /** filter forevent name */ - eventName?: string - /** event name to be used for custom message if the subscription functions returns value */ - emitEventName?: string - /** filter for principal id */ - principalId?: PrincipalId - /** filter for tenant id */ - tenantId?: TenantId - /** hooks of subscription */ - hooks: { - transformInput?: { - transformInputSchema: Schema - transformParameterSchema: Schema - transformFunction: SubscriptionTransformInputHook - } - beforeGuard?: Record< - string, - SubscriptionBeforeGuardHook - > - afterGuard?: Record< - string, - SubscriptionAfterGuardHook< - ServiceClassType, - FunctionResultType, - FunctionPayloadType, - FunctionParamsType, - Invokes, - EmitListType - > - > - transformOutput?: { - transformOutputSchema: Schema - transformFunction: SubscriptionTransformOutputHook - } - } - invokes: FromInvokeToOtherType - emitList: EmitListType + /** the name of the subscription */ + subscriptionName: string + /** the description of the subscription */ + subscriptionDescription: string + /** the metadata of the subscription */ + metadata: MetadataType + /** config information for event bridge */ + eventBridgeConfig: DefinitionEventBridgeConfig + /** the subscription function */ + call: SubscriptionFunction< + S, + FunctionPayloadType, + FunctionParamsType, + FunctionOutputType, + Resources, + Invokes, + EmitList + > + /** filter for messages produced by given sender */ + sender?: { + serviceName?: string + serviceVersion?: string + serviceTarget?: string + instanceId?: InstanceId + } + /** filter for messages consumed by given receiver */ + receiver?: { + serviceName?: string + serviceVersion?: string + serviceTarget?: string + instanceId?: InstanceId + } + /** filter for message type */ + messageType?: EBMessageType + /** filter forevent name */ + eventName?: string + /** event name to be used for custom message if the subscription functions returns value */ + emitEventName?: string + /** filter for principal id */ + principalId?: PrincipalId + /** filter for tenant id */ + tenantId?: TenantId + /** hooks of subscription */ + hooks: { + transformInput?: { + transformInputSchema: Schema + transformParameterSchema: Schema + transformFunction: SubscriptionTransformInputHook< + S, + TransformInputPayload, + TransformInputParams, + FunctionPayloadType, + FunctionParamsType + > + } + beforeGuard?: Record< + string, + SubscriptionBeforeGuardHook + > + afterGuard?: Record< + string, + SubscriptionAfterGuardHook< + S, + FunctionPayloadType, + FunctionParamsType, + FunctionOutputType, + Resources, + Invokes, + EmitList + > + > + transformOutput?: { + transformOutputSchema: Schema + transformFunction: SubscriptionTransformOutputHook< + S, + FinalFunctionOutputType, + FunctionParamsType, + TransformOutputHookOutput + > + } + } + invokes: FromInvokeToOtherType< + Invokes, + { outputSchema?: SchemaObject; payloadSchema?: SchemaObject; parameterSchema?: SchemaObject } + > + emitList: FromEmitToOtherType + deprecated: boolean } diff --git a/packages/core/src/core/types/subscription/SubscriptionDefinitionList.ts b/packages/core/src/core/types/subscription/SubscriptionDefinitionList.ts index 3ebacf294..5a85c62ca 100644 --- a/packages/core/src/core/types/subscription/SubscriptionDefinitionList.ts +++ b/packages/core/src/core/types/subscription/SubscriptionDefinitionList.ts @@ -1,4 +1,4 @@ -import type { ServiceClass } from '../ServiceClass.js' +import type { Service } from '../../Service/index.js' import type { SubscriptionDefinition } from './SubscriptionDefinition.js' /** @@ -8,19 +8,21 @@ import type { SubscriptionDefinition } from './SubscriptionDefinition.js' * export const userServiceCommands: SubscriptionDefinitionList = [signUp.getDefinition()] * ``` */ -export type SubscriptionDefinitionList = Promise< - SubscriptionDefinition +export type SubscriptionDefinitionList = Promise< + SubscriptionDefinition >[] -export type SubscriptionDefinitionListResolved = SubscriptionDefinition< - ServiceClassType, - any, - any, - any, - any, - any, - any, - any, - any, - any +export type SubscriptionDefinitionListResolved = SubscriptionDefinition< + ServiceClassType, + any, + any, + any, + any, + any, + any, + any, + any, + any, + any, + any >[] diff --git a/packages/core/src/core/types/subscription/SubscriptionDefinitionMetadataBase.ts b/packages/core/src/core/types/subscription/SubscriptionDefinitionMetadataBase.ts index 2a9239981..a9b433faa 100644 --- a/packages/core/src/core/types/subscription/SubscriptionDefinitionMetadataBase.ts +++ b/packages/core/src/core/types/subscription/SubscriptionDefinitionMetadataBase.ts @@ -3,13 +3,13 @@ import type { SchemaObject } from 'openapi3-ts/oas31' import type { ContentType } from '../ContentType.js' export type SubscriptionDefinitionMetadataBase = { - expose: { - contentTypeRequest?: ContentType - contentEncodingRequest?: string - contentTypeResponse?: ContentType - contentEncodingResponse?: string - inputPayload?: SchemaObject - outputPayload?: SchemaObject - parameter?: SchemaObject - } + expose: { + contentTypeRequest?: ContentType + contentEncodingRequest?: string + contentTypeResponse?: ContentType + contentEncodingResponse?: string + inputPayload?: SchemaObject + outputPayload?: SchemaObject + parameter?: SchemaObject + } } diff --git a/packages/core/src/core/types/subscription/SubscriptionFunction.ts b/packages/core/src/core/types/subscription/SubscriptionFunction.ts index 7e2e5b8c1..4217e713e 100644 --- a/packages/core/src/core/types/subscription/SubscriptionFunction.ts +++ b/packages/core/src/core/types/subscription/SubscriptionFunction.ts @@ -1,4 +1,7 @@ -import type { ServiceClass } from '../ServiceClass.js' +import type { Schema } from '@typeschema/main' +import type { Service } from '../../Service/index.js' +import type { EmptyObject } from '../EmptyObject.js' +import type { InvokeList } from '../InvokeList.js' import type { SubscriptionFunctionContext } from './SubscriptionFunctionContext.js' /** * CommandFunction is a function which will be triggered when a matching event bridge message is received by the service @@ -6,17 +9,16 @@ import type { SubscriptionFunctionContext } from './SubscriptionFunctionContext. * @group Subscription */ export type SubscriptionFunction< - ServiceClassType extends ServiceClass, - MessagePayloadType = unknown, - MessageParamsType = undefined, - FunctionPayloadType = MessagePayloadType, - FunctionParamsType = MessageParamsType, - FunctionResultType = undefined, - Invokes = {}, - EmitListType = {}, + ServiceClassType extends Service, + FunctionPayloadType = unknown, + FunctionParamsType = unknown, + FunctionOutputType = unknown, + Resources extends Record = EmptyObject, + Invokes extends InvokeList = EmptyObject, + EmitList extends Record = EmptyObject, > = ( - this: ServiceClassType, - context: SubscriptionFunctionContext, - payload: Readonly, - parameter: Readonly, -) => Promise + this: ServiceClassType, + context: SubscriptionFunctionContext, + payload: Readonly, + parameter: Readonly, +) => Promise diff --git a/packages/core/src/core/types/subscription/SubscriptionFunctionContext.ts b/packages/core/src/core/types/subscription/SubscriptionFunctionContext.ts index 74eff42c8..136f6c360 100644 --- a/packages/core/src/core/types/subscription/SubscriptionFunctionContext.ts +++ b/packages/core/src/core/types/subscription/SubscriptionFunctionContext.ts @@ -1,7 +1,9 @@ +import type { Schema } from '@typeschema/main' import type { ContextBase } from '../ContextBase.js' import type { EBMessage } from '../EBMessage.js' import type { EmitCustomMessageFunction } from '../EmitCustomMessageFunction.js' -import type { InvokeFunction } from '../InvokeFunction.js' +import type { EmptyObject } from '../EmptyObject.js' +import type { InvokeList } from '../InvokeList.js' import type { Prettify } from '../Prettify.js' /** @@ -13,49 +15,35 @@ import type { Prettify } from '../Prettify.js' * * @group Subscription */ -export type SubscriptionFunctionContextEnhancements = { - /** the original message */ - message: Readonly - /** emit a custom message */ - emit: EmitCustomMessageFunction - /** - * @deprecated Please use service instead and define the invocations with canInvoke in subscription builder. - * - * Invokes a command and returns the result. - * It is recommended to validate the result against a schema which only contains the data you actually need. - * - * @example - * ```typescript - * - * const address: EBMessageAddress = { - * serviceName: 'name-of-service-to-invoke', - * serviceVersion: '1', - * serviceTarget: 'command-name-to-invoke', - * } - * - * const inputPayload = { my: 'input' } - * const inputParameter = { search: 'for_me' } - * - * const result = await invoke(address, inputPayload inputParameter ) - * ``` - */ - invoke: InvokeFunction - /** - * Invokes a command and returns the result. - * It is recommended to validate the result against a schema which only contains the data you actually need. - * - * @example - * ```typescript - * // define your invocation in subscription builder - * .canInvoke<{ response: string }>('ServiceA', '1', 'test', payloadSchema, parameterSchema) - * .setCommandFunction(async function (context, payload, _parameter) { - * const inputPayload = { my: 'input' } - * const inputParameter = { search: 'for_me' } - * const result = await context.service.ServiceA[1].test(inputPayload,inputParameter) - * }) - * ``` - */ - service: Invokes +export type SubscriptionFunctionContextEnhancements< + Resources extends Record = EmptyObject, + Invokes extends InvokeList = EmptyObject, + EmitList extends Record = EmptyObject, +> = { + /** the original message */ + message: Readonly + /** emit a custom message */ + emit: EmitCustomMessageFunction + /** + * Invokes a command and returns the result. + * It is recommended to validate the result against a schema which only contains the data you actually need. + * + * @example + * ```typescript + * // define your invocation in subscription builder + * .canInvoke<{ response: string }>('ServiceA', '1', 'test', payloadSchema, parameterSchema) + * .setCommandFunction(async function (context, payload, _parameter) { + * const inputPayload = { my: 'input' } + * const inputParameter = { search: 'for_me' } + * const result = await context.service.ServiceA[1].test(inputPayload,inputParameter) + * }) + * ``` + */ + service: Invokes + /** + * Provides resources defined in service builder and set via config during service creation + */ + resources: Resources } /** @@ -63,6 +51,8 @@ export type SubscriptionFunctionContextEnhancements = Prettify< - ContextBase & SubscriptionFunctionContextEnhancements -> +export type SubscriptionFunctionContext< + Resources extends Record = EmptyObject, + Invokes extends InvokeList = EmptyObject, + EmitList extends Record = EmptyObject, +> = Prettify> diff --git a/packages/core/src/core/types/subscription/SubscriptionTransformFunctionContext.ts b/packages/core/src/core/types/subscription/SubscriptionTransformFunctionContext.ts index 7126ace69..15bc2c3e9 100644 --- a/packages/core/src/core/types/subscription/SubscriptionTransformFunctionContext.ts +++ b/packages/core/src/core/types/subscription/SubscriptionTransformFunctionContext.ts @@ -1,13 +1,15 @@ import type { ContextBase } from '../ContextBase.js' import type { EBMessage } from '../EBMessage.js' +import type { EmptyObject } from '../EmptyObject.js' import type { Prettify } from '../Prettify.js' /** * @group Subscription */ -export type SubscriptionTransformFunctionContext = Prettify< - ContextBase & { - /** the original received message */ - message: Readonly - } +export type SubscriptionTransformFunctionContext = EmptyObject> = Prettify< + ContextBase & { + /** the original received message */ + message: Readonly + resources: Resources + } > diff --git a/packages/core/src/core/types/subscription/SubscriptionTransformInputHook.ts b/packages/core/src/core/types/subscription/SubscriptionTransformInputHook.ts index a3de78b73..178328046 100644 --- a/packages/core/src/core/types/subscription/SubscriptionTransformInputHook.ts +++ b/packages/core/src/core/types/subscription/SubscriptionTransformInputHook.ts @@ -1,20 +1,21 @@ +import type { Service } from '../../Service/Service.impl.js' import type { SubscriptionTransformFunctionContext } from './SubscriptionTransformFunctionContext.js' /** * @group Subscription */ export type SubscriptionTransformInputHook< - ServiceClassType, - PayloadOutput = unknown, - ParamsOutput = unknown, - PayloadInput = unknown, - ParamsInput = unknown, + S extends Service, + TransformInputPayload, + TransformInputParams, + FunctionPayloadType, + FunctionParamsType, > = ( - this: ServiceClassType, - context: SubscriptionTransformFunctionContext, - payload: Readonly, - parameter: Readonly, + this: S, + context: SubscriptionTransformFunctionContext, + payload: Readonly, + parameter: Readonly, ) => Promise<{ - payload: Readonly - parameter: Readonly + payload: Readonly + parameter: Readonly }> diff --git a/packages/core/src/core/types/subscription/SubscriptionTransformOutputHook.ts b/packages/core/src/core/types/subscription/SubscriptionTransformOutputHook.ts index 1b9ccb28c..639035cc1 100644 --- a/packages/core/src/core/types/subscription/SubscriptionTransformOutputHook.ts +++ b/packages/core/src/core/types/subscription/SubscriptionTransformOutputHook.ts @@ -1,3 +1,4 @@ +import type { Service } from '../../Service/index.js' import type { SubscriptionTransformFunctionContext } from './SubscriptionTransformFunctionContext.js' /** @@ -6,13 +7,13 @@ import type { SubscriptionTransformFunctionContext } from './SubscriptionTransfo * @group Subscription */ export type SubscriptionTransformOutputHook< - ServiceClassType, - MessageResultType = unknown, - MessageParamsType = unknown, - ResponseOutput = unknown, + S extends Service, + FinalFunctionOutputType, + FunctionParamsType, + TransformOutputHookOutput, > = ( - this: ServiceClassType, - context: SubscriptionTransformFunctionContext, - payload: Readonly, - parameter: Readonly, -) => Promise + this: S, + context: SubscriptionTransformFunctionContext, + payload: Readonly, + parameter: Readonly, +) => Promise diff --git a/packages/core/src/helper/convertEmitValidationsToSchema.impl.ts b/packages/core/src/helper/convertEmitValidationsToSchema.impl.ts new file mode 100644 index 000000000..38d5fb9c7 --- /dev/null +++ b/packages/core/src/helper/convertEmitValidationsToSchema.impl.ts @@ -0,0 +1,23 @@ +import type { Schema } from '@typeschema/main' +import type { SchemaObject } from 'openapi3-ts/oas31' + +import type { FromEmitToOtherType } from '../core/types/index.js' +import { validationToSchema } from '../zodOpenApi/validationToSchema.js' + +type InputType = { + [key: string]: Schema +} + +export const convertEmitValidationsToSchema = async ( + emits: T, +): Promise> => { + const result: { + [key: string]: SchemaObject + } = {} + + for (const [key, schema] of Object.entries(emits)) { + result[key] = (await validationToSchema(schema)) as SchemaObject + } + + return result as FromEmitToOtherType +} diff --git a/packages/core/src/helper/convertInvokeValidationsToSchema.impl.ts b/packages/core/src/helper/convertInvokeValidationsToSchema.impl.ts new file mode 100644 index 000000000..980f29a54 --- /dev/null +++ b/packages/core/src/helper/convertInvokeValidationsToSchema.impl.ts @@ -0,0 +1,70 @@ +import { type Schema as ValidationSchema, toJSONSchema } from '@typeschema/main' +import type { SchemaObject } from 'openapi3-ts/oas31' + +import type { FromInvokeToOtherType } from '../core/types/index.js' + +type InputType = { + [serviceName: string]: { + [serviceVersion: string]: { + [name: string]: { + outputSchema?: ValidationSchema + payloadSchema?: ValidationSchema + parameterSchema?: ValidationSchema + } + } + } +} + +type ResultType = { + [serviceName: string]: { + [serviceVersion: string]: { + [name: string]: { + outputSchema?: SchemaObject + payloadSchema?: SchemaObject + parameterSchema?: SchemaObject + } + } + } +} + +export const convertInvokeValidationsToSchema = async ( + invokes: T, +): Promise< + FromInvokeToOtherType< + T, + { outputSchema?: SchemaObject; payloadSchema?: SchemaObject; parameterSchema?: SchemaObject } + > +> => { + const result: ResultType = {} + + for (const [serviceName, versions] of Object.entries(invokes)) { + result[serviceName] = { ...result[serviceName] } + for (const [serviceVersion, commands] of Object.entries(versions)) { + result[serviceName][serviceVersion] = { ...result[serviceName][serviceVersion] } + for (const [command, schemas] of Object.entries(commands)) { + result[serviceName][serviceVersion][command] = { ...result[serviceName][serviceVersion][command] } + const [outputSchema, payloadSchema, parameterSchema] = await Promise.all([ + schemas.outputSchema + ? toJSONSchema(schemas.outputSchema) + : new Promise(resolve => resolve(undefined)), + schemas.payloadSchema + ? toJSONSchema(schemas.payloadSchema) + : new Promise(resolve => resolve(undefined)), + schemas.parameterSchema + ? toJSONSchema(schemas.parameterSchema) + : new Promise(resolve => resolve(undefined)), + ]) + + result[serviceName][serviceVersion][command] = { + outputSchema: outputSchema as SchemaObject, + payloadSchema: payloadSchema as SchemaObject, + parameterSchema: parameterSchema as SchemaObject, + } + } + } + } + return result as FromInvokeToOtherType< + T, + { outputSchema?: SchemaObject; payloadSchema?: SchemaObject; parameterSchema?: SchemaObject } + > +} diff --git a/packages/core/src/helper/exportServiceDefinitions.ts b/packages/core/src/helper/exportServiceDefinitions.ts new file mode 100644 index 000000000..4f8a42af6 --- /dev/null +++ b/packages/core/src/helper/exportServiceDefinitions.ts @@ -0,0 +1,82 @@ +import type { ServiceBuilder } from '../ServiceBuilder/index.js' +import { puristaVersion } from '../version.js' +import type { FullDefinition } from './types/FullDefinition.js' +import type { FullServiceDefinition } from './types/FullServiceDefinition.js' +import type { ServiceDefinitions } from './types/ServiceDefinitions.js' + +/** + * Merge service definitions into one big full service definition structure + * @param existing + * @param defintionToAdd + * @returns + */ +const mergeServiceDefintion = ( + existing: FullServiceDefinition, + defintionToAdd: ServiceDefinitions, +): T => { + const commands = defintionToAdd.commands.reduce((current, definition) => { + return { + // biome-ignore lint/performance/noAccumulatingSpread: + ...current, + [definition.commandName]: definition, + } + }, {}) + + const subscriptions = defintionToAdd.subscriptions.reduce((current, definition) => { + return { + // biome-ignore lint/performance/noAccumulatingSpread: + ...current, + [definition.subscriptionName]: definition, + } + }, {}) + + const ret = { ...existing } + + if (!ret[defintionToAdd.serviceName]) { + ret[defintionToAdd.serviceName] = { + [defintionToAdd.serviceVersion]: { + description: defintionToAdd.serviceDescription, + commands, + subscriptions, + deprecated: defintionToAdd.deprecated, + }, + } + } + + if (!ret[defintionToAdd.serviceName][defintionToAdd.serviceVersion]) { + ret[defintionToAdd.serviceName][defintionToAdd.serviceVersion] = { + description: defintionToAdd.serviceDescription, + commands, + subscriptions, + deprecated: defintionToAdd.deprecated, + } + } + + ret[defintionToAdd.serviceName][defintionToAdd.serviceVersion] = { + ...ret[defintionToAdd.serviceName][defintionToAdd.serviceVersion], + description: defintionToAdd.serviceDescription, + commands, + subscriptions, + deprecated: defintionToAdd.deprecated, + } + + return ret as T +} + +/** + * Exports the service definitions. + * Includes the information about commands and subscriptions. + * + * The output can be saves as JSON string in a file. + * + * @param serviceBuilders + * @returns + */ +export const exportServiceDefinitions = async (serviceBuilders: ServiceBuilder[]): Promise => { + const serviceDefinitions = await Promise.all(serviceBuilders.map(builder => builder.getFullServiceDefintion())) + + return { + version: puristaVersion, + services: serviceDefinitions.reduce((def, current) => mergeServiceDefintion(def, current), {}), + } +} diff --git a/packages/core/src/helper/getTimeoutPromise.impl.ts b/packages/core/src/helper/getTimeoutPromise.impl.ts index 3122608ea..677793a83 100644 --- a/packages/core/src/helper/getTimeoutPromise.impl.ts +++ b/packages/core/src/helper/getTimeoutPromise.impl.ts @@ -7,14 +7,14 @@ import { StatusCode, UnhandledError } from '../core/index.js' * @returns */ export const getTimeoutPromise = (fn: Promise, ttl = 30000): Promise => { - let timeout: ReturnType - const ttlPromise = new Promise((_resolve, reject) => { - timeout = setTimeout(() => { - const err = new UnhandledError(StatusCode.GatewayTimeout, 'invocation timed out') - clearTimeout(timeout) - reject(err) - }, ttl) - }) + let timeout: ReturnType + const ttlPromise = new Promise((_resolve, reject) => { + timeout = setTimeout(() => { + const err = new UnhandledError(StatusCode.GatewayTimeout, 'invocation timed out') + clearTimeout(timeout) + reject(err) + }, ttl) + }) - return Promise.race([fn, ttlPromise]) + return Promise.race([fn, ttlPromise]) } diff --git a/packages/core/src/helper/getTimeoutPromise.test.ts b/packages/core/src/helper/getTimeoutPromise.test.ts index cf1495f1e..836697f24 100644 --- a/packages/core/src/helper/getTimeoutPromise.test.ts +++ b/packages/core/src/helper/getTimeoutPromise.test.ts @@ -5,40 +5,40 @@ import { StatusCode, UnhandledError } from '../core/index.js' import { getTimeoutPromise } from './getTimeoutPromise.impl.js' describe('getTimeoutPromise', () => { - let sandbox: SinonSandbox - let clock: SinonFakeTimers + let sandbox: SinonSandbox + let clock: SinonFakeTimers - beforeEach(() => { - sandbox = createSandbox() - clock = sandbox.useFakeTimers() - }) + beforeEach(() => { + sandbox = createSandbox() + clock = sandbox.useFakeTimers() + }) - afterEach(() => { - sandbox.restore() - }) + afterEach(() => { + sandbox.restore() + }) - test('should resolve with the result of the promise if it resolves within the TTL', async () => { - const promise = Promise.resolve('foo') - const result = await getTimeoutPromise(promise, 5000) + test('should resolve with the result of the promise if it resolves within the TTL', async () => { + const promise = Promise.resolve('foo') + const result = await getTimeoutPromise(promise, 5000) - expect(result).toBe('foo') - }) + expect(result).toBe('foo') + }) - test('should reject with a UnhandledError if the promise takes too long to resolve', async () => { - const promise = new Promise((resolve) => setTimeout(() => resolve('foo'), 10000)) - const timeout = 50 + test('should reject with a UnhandledError if the promise takes too long to resolve', async () => { + const promise = new Promise(resolve => setTimeout(() => resolve('foo'), 10000)) + const timeout = 50 - const promiseSpy = sandbox.stub() - const errorSpy = sandbox.stub() + const promiseSpy = sandbox.stub() + const errorSpy = sandbox.stub() - const result = getTimeoutPromise(promise, timeout).then(promiseSpy).catch(errorSpy) + const result = getTimeoutPromise(promise, timeout).then(promiseSpy).catch(errorSpy) - clock.tick(timeout + 1) - await result + clock.tick(timeout + 1) + await result - expect(errorSpy.firstCall.firstArg).toStrictEqual( - new UnhandledError(StatusCode.GatewayTimeout, 'invocation timed out'), - ) - expect(promiseSpy.notCalled).toBeTruthy() - }) + expect(errorSpy.firstCall.firstArg).toStrictEqual( + new UnhandledError(StatusCode.GatewayTimeout, 'invocation timed out'), + ) + expect(promiseSpy.notCalled).toBeTruthy() + }) }) diff --git a/packages/core/src/helper/gracefulShutdown.impl.ts b/packages/core/src/helper/gracefulShutdown.impl.ts index 0c4453994..19b5e986b 100644 --- a/packages/core/src/helper/gracefulShutdown.impl.ts +++ b/packages/core/src/helper/gracefulShutdown.impl.ts @@ -35,43 +35,43 @@ import type { ShutdownEntry } from './types/index.js' * @group Helper */ export const gracefulShutdown = (logger: Logger, list: ShutdownEntry[], timeoutMs = 30000) => { - process.once('SIGTERM', async () => shutDown('SIGTERM')) - process.once('SIGINT', async () => shutDown('SIGINT')) - process.once('SIGQUIT', async () => shutDown('SIGQUIT')) + process.once('SIGTERM', async () => shutDown('SIGTERM')) + process.once('SIGINT', async () => shutDown('SIGINT')) + process.once('SIGQUIT', async () => shutDown('SIGQUIT')) - const shutDown = async (signal = 'SIGTERM') => { - logger.info(`start graceful shut down because of kill signal (${signal})`) + const shutDown = async (signal = 'SIGTERM') => { + logger.info(`start graceful shut down because of kill signal (${signal})`) - const timer = setTimeout(() => { - logger.error(`shutdown timeout - force kill`) - process.exit(1) - }, timeoutMs) + const timer = setTimeout(() => { + logger.error('shutdown timeout - force kill') + process.exit(1) + }, timeoutMs) - let hasError = false + let hasError = false - for (const entry of list) { - try { - await entry.destroy() - await new Promise((resolve) => - setTimeout(() => { - resolve(undefined) - }, 0), - ) - logger.info(`${entry.name} shutdown successfully`) - } catch (err) { - logger.error({ err }, `error on shutdown ${entry.name}`) - hasError = true - } - } + for (const entry of list) { + try { + await entry.destroy() + await new Promise(resolve => + setTimeout(() => { + resolve(undefined) + }, 0), + ) + logger.info(`${entry.name} shutdown successfully`) + } catch (err) { + logger.error({ err }, `error on shutdown ${entry.name}`) + hasError = true + } + } - clearTimeout(timer) + clearTimeout(timer) - if (hasError) { - logger.info('shut down finished with errors') - process.exit(1) - } + if (hasError) { + logger.info('shut down finished with errors') + process.exit(1) + } - logger.info('graceful shut down finished successfully') - process.kill(process.pid, signal) - } + logger.info('graceful shut down finished successfully') + process.kill(process.pid, signal) + } } diff --git a/packages/core/src/helper/index.ts b/packages/core/src/helper/index.ts index 42d07dff6..543a16d5a 100644 --- a/packages/core/src/helper/index.ts +++ b/packages/core/src/helper/index.ts @@ -1,6 +1,10 @@ +export * from './convertEmitValidationsToSchema.impl.js' +export * from './convertInvokeValidationsToSchema.impl.js' +export * from './exportServiceDefinitions.js' export * from './getTimeoutPromise.impl.js' export * from './gracefulShutdown.impl.js' export * from './safeBind.impl.js' +export * from './schemaObjectToTsType/transform.js' export * from './string/index.js' export * from './throwIfNotValidMessage.impl.js' export * from './types/index.js' diff --git a/packages/core/src/helper/safeBind.impl.ts b/packages/core/src/helper/safeBind.impl.ts index 700af428e..76451e5cb 100644 --- a/packages/core/src/helper/safeBind.impl.ts +++ b/packages/core/src/helper/safeBind.impl.ts @@ -1,9 +1,3 @@ -type SafeThisParameterType = T extends (this: unknown, ...args: any[]) => any - ? never - : T extends (this: infer U, ...args: any[]) => any - ? U - : never - /** * Bind `this` argument like regular `.bind(thisArg)`, but keeps the typescript types in result * @@ -16,9 +10,9 @@ type SafeThisParameterType = T extends (this: unknown, ...args: any[]) => any * @param thisArg * @returns */ -export function safeBind any>( - fn: T, - thisArg: SafeThisParameterType, +export function safeBind any, U>( + fn: T, + thisArg: U, ): (...args: Parameters) => ReturnType { - return fn.bind(thisArg) + return fn.bind(thisArg) } diff --git a/packages/core/src/helper/schemaObjectToTsType/transform.ts b/packages/core/src/helper/schemaObjectToTsType/transform.ts new file mode 100644 index 000000000..de342aac6 --- /dev/null +++ b/packages/core/src/helper/schemaObjectToTsType/transform.ts @@ -0,0 +1,323 @@ +import type { ReferenceObject, SchemaObject } from 'openapi3-ts/oas31' + +import type { GlobalContext } from './types.js' +import { + escObjKey, + escStr, + getEntries, + getSchemaObjectComment, + indent, + parseRef, + tsArrayOf, + tsIntersectionOf, + tsOmit, + tsOneOf, + tsOptionalProperty, + tsReadonly, + tsTupleOf, + tsUnionOf, + tsWithRequired, +} from './utils.js' + +export interface TransformSchemaObjectOptions { + /** The full ID for this object (mostly used in error messages) */ + path: string + /** Shared context */ + ctx: GlobalContext +} + +export function schemaObjectToTsType(schemaObject: SchemaObject | ReferenceObject, options?: Partial) { + const ctx: GlobalContext = { + additionalProperties: options?.additionalProperties ?? false, + alphabetize: options?.alphabetize ?? false, + defaultNonNullable: options?.defaultNonNullable ?? false, + discriminators: {}, + immutableTypes: options?.immutableTypes ?? false, + emptyObjectsUnknown: options?.emptyObjectsUnknown ?? false, + indentLv: 0, + operations: {}, + pathParamsAsTypes: options?.pathParamsAsTypes ?? false, + parameters: {}, + silent: options?.silent ?? false, + supportArrayLength: options?.supportArrayLength ?? false, + excludeDeprecated: options?.excludeDeprecated ?? false, + } + + return transformSchemaObject(schemaObject, ctx, '') +} + +export function transformSchemaObject( + schemaObject: SchemaObject | ReferenceObject, + ctx: GlobalContext, + path: string, +): string { + let { indentLv } = ctx + + // const fallback (primitives) return passed value + if (!schemaObject || typeof schemaObject !== 'object') { + return schemaObject + } + // const fallback (array) return tuple + if (Array.isArray(schemaObject)) { + const finalType = tsTupleOf(...schemaObject) + return ctx.immutableTypes ? tsReadonly(finalType) : finalType + } + + // $ref + if ('$ref' in schemaObject) { + return schemaObject.$ref + } + + // const (valid for any type) + const s = schemaObject as any + if (s.const !== null && s.const !== undefined) { + let schemaConst = s.const as any + if ('type' in s) { + if (s.type === 'string') { + schemaConst = escStr(schemaConst) + } + } + return transformSchemaObject( + schemaConst, + { ...ctx, immutableTypes: false, indentLv: indentLv + 1 }, + path, + // note: guarantee readonly happens once, here + ) + } + + // enum (valid for any type) + if (schemaObject.enum) { + let items = schemaObject.enum as any[] + if ('type' in schemaObject) { + if ( + schemaObject.type === 'string' || + (Array.isArray(schemaObject.type) && schemaObject.type.includes('string')) + ) { + items = items.map(t => escStr(t || '')) + } // empty/missing values are empty strings + } else { + // if no type, assume "string" + items = items.map(t => escStr(t || '')) + } + return tsUnionOf( + ...items, + /** + ...(schemaObject.nullable ?? + ('type' in schemaObject && Array.isArray(schemaObject.type) && schemaObject.type.includes('null')) + ? ['null'] + : []), + */ + ...('type' in schemaObject && Array.isArray(schemaObject.type) && schemaObject.type.includes('null') + ? ['null'] + : []), + ) + } + + // oneOf (no discriminator) + if ('oneOf' in schemaObject && !schemaObject.oneOf?.some(t => '$ref' in t && ctx.discriminators[t.$ref])) { + const maybeTypes = schemaObject.oneOf?.map(item => transformSchemaObject(item, ctx, path)) ?? [] + if (maybeTypes.some(t => typeof t === 'string' && t.includes('{'))) { + return tsOneOf(...maybeTypes) + } // OneOf<> helper needed if any objects present ("{") + return tsUnionOf(...maybeTypes) // otherwise, TS union works for primitives + } + + if ('type' in schemaObject) { + // array type + if (Array.isArray(schemaObject.type)) { + return tsOneOf(...schemaObject.type.map(t => transformSchemaObject({ ...schemaObject, type: t }, ctx, path))) + } + /* + // "type": "null" + if (schemaObject.type === 'null') { + return schemaObject.type + } + */ + + // "type": "string" / "type": "null" + if (schemaObject.type === 'string' || schemaObject.type === 'boolean') { + // return schemaObject.nullable ? tsUnionOf(schemaObject.type, 'null') : schemaObject.type + return schemaObject.type + } + + // "type": "number" / "type": "integer" + if (schemaObject.type === 'number' || schemaObject.type === 'integer') { + // return schemaObject.nullable ? tsUnionOf('number', 'null') : 'number' + return 'number' + } + + // "type": "array" + if (schemaObject.type === 'array') { + indentLv++ + let itemType = 'unknown' + let isTupleType = false + if (schemaObject.items) { + if (Array.isArray(schemaObject.items)) { + // tuple type support + isTupleType = true + const result: string[] = [] + for (const item of schemaObject.items) { + result.push(transformSchemaObject(item, { ...ctx, indentLv }, path)) + } + itemType = `[${result.join(',')}]` + } else { + itemType = transformSchemaObject(schemaObject.items, { ...ctx, indentLv }, path) + } + } + const minItems: number = + typeof schemaObject.minItems === 'number' && schemaObject.minItems >= 0 ? schemaObject.minItems : 0 + const maxItems: number | undefined = + typeof schemaObject.maxItems === 'number' && schemaObject.maxItems >= 0 && minItems <= schemaObject.maxItems + ? schemaObject.maxItems + : undefined + const estimateCodeSize = + typeof maxItems !== 'number' ? minItems : (maxItems * (maxItems + 1) - minItems * (minItems - 1)) / 2 + // export types + if (ctx.supportArrayLength && (minItems !== 0 || maxItems !== undefined) && estimateCodeSize < 30) { + if (typeof schemaObject.maxItems !== 'number') { + itemType = tsTupleOf(...Array.from({ length: minItems }).map(() => itemType), `...${tsArrayOf(itemType)}`) + return ctx.immutableTypes || schemaObject.readOnly ? tsReadonly(itemType) : itemType + } + return tsUnionOf( + ...Array.from({ length: (maxItems ?? 0) - minItems + 1 }) + .map((_, i) => i + minItems) + .map(n => { + const t = tsTupleOf(...Array.from({ length: n }).map(() => itemType)) + return ctx.immutableTypes || schemaObject.readOnly ? tsReadonly(t) : t + }), + ) + } + if (!isTupleType) { + // Do not use tsArrayOf when it is a tuple type + itemType = tsArrayOf(itemType) + } + itemType = ctx.immutableTypes || schemaObject.readOnly ? tsReadonly(itemType) : itemType + // return schemaObject.nullable ? tsUnionOf(itemType, 'null') : itemType + return itemType + } + } + + // "type": "object" (explicit) + // "anyOf" / "allOf" (object type implied) + + // core type: properties + additionalProperties + const coreType: string[] = [] + if ( + ('properties' in schemaObject && schemaObject.properties && Object.keys(schemaObject.properties).length) ?? + ('additionalProperties' in schemaObject && schemaObject.additionalProperties) + ) { + indentLv++ + for (const [k, v] of getEntries(schemaObject.properties ?? {}, ctx.alphabetize, ctx.excludeDeprecated)) { + const c = getSchemaObjectComment(v as SchemaObject, indentLv) + if (c) { + coreType.push(indent(c, indentLv)) + } + let key = escObjKey(k) + let isOptional = !Array.isArray(schemaObject.required) || !schemaObject.required.includes(k) + if (isOptional && ctx.defaultNonNullable && 'default' in v) { + isOptional = false + } // if --default-non-nullable specified and this has a default, it’s no longer optional + if (isOptional) { + key = tsOptionalProperty(key) + } + if (ctx.immutableTypes || schemaObject.readOnly) { + key = tsReadonly(key) + } + coreType.push(indent(`${key}: ${transformSchemaObject(v, { ...ctx, indentLv }, path)};`, indentLv)) + } + if (schemaObject.additionalProperties ?? ctx.additionalProperties) { + let addlType = 'unknown' + if (typeof schemaObject.additionalProperties === 'object') { + if (!Object.keys(schemaObject.additionalProperties).length) { + addlType = 'unknown' + } else { + addlType = transformSchemaObject( + schemaObject.additionalProperties as SchemaObject, + { ...ctx, indentLv }, + path, + ) + } + } + coreType.push(indent(`[key: string]: ${tsUnionOf(addlType || 'unknown', 'undefined')};`, indentLv)) // note: `| undefined` is required to mesh with possibly-undefined keys + } + indentLv-- + } + + // discriminators + for (const k of ['oneOf', 'allOf', 'anyOf'] as ('oneOf' | 'allOf' | 'anyOf')[]) { + if (!(k in schemaObject)) { + continue + } + const discriminatorRef: ReferenceObject | undefined = (schemaObject as any)[k].find( + (t: SchemaObject | ReferenceObject) => '$ref' in t && ctx.discriminators[t.$ref], + ) + if (discriminatorRef) { + const discriminator = ctx.discriminators[discriminatorRef.$ref] + let value = parseRef(path).path.pop() + if (discriminator.mapping) { + // Mapping value can either be a fully-qualified ref (#/components/schemas/XYZ) or a schema name (XYZ) + const matchedValue = Object.entries(discriminator.mapping).find( + ([_, v]) => (!v.startsWith('#') && v === value) || (v.startsWith('#') && parseRef(v).path.pop() === value), + ) + if (matchedValue) { + value = matchedValue[0] + } // why was this designed backwards!? + } + coreType.unshift(indent(`${discriminator.propertyName}: ${escStr(value)};`, indentLv + 1)) + break + } + } + + // close coreType + let finalType = coreType.length ? `{\n${coreType.join('\n')}\n${indent('}', indentLv)}` : '' + + /** collect oneOf/allOf/anyOf with Omit<> for discriminators */ + function collectCompositions(items: (SchemaObject | ReferenceObject)[]): string[] { + const output: string[] = [] + for (const item of items) { + const itemType = transformSchemaObject(item, { ...ctx, indentLv }, path) + if ('$ref' in item && ctx.discriminators[item.$ref]) { + output.push(tsOmit(itemType, [ctx.discriminators[item.$ref].propertyName])) + continue + } + output.push(itemType) + } + return output + } + // oneOf (discriminator) + if ('oneOf' in schemaObject && Array.isArray(schemaObject.oneOf)) { + const oneOfType = tsOneOf(...collectCompositions(schemaObject.oneOf)) + finalType = finalType ? tsIntersectionOf(finalType, oneOfType) : oneOfType + } else { + // allOf + if ('allOf' in schemaObject && Array.isArray(schemaObject.allOf)) { + finalType = tsIntersectionOf(...(finalType ? [finalType] : []), ...collectCompositions(schemaObject.allOf)) + if ('required' in schemaObject && Array.isArray(schemaObject.required)) { + finalType = tsWithRequired(finalType, schemaObject.required) + } + } + // anyOf + if ('anyOf' in schemaObject && Array.isArray(schemaObject.anyOf)) { + const anyOfTypes = tsUnionOf(...collectCompositions(schemaObject.anyOf)) + finalType = finalType ? tsIntersectionOf(finalType, anyOfTypes) : anyOfTypes + } + } + + // nullable (3.0) + /* + if (schemaObject.nullable) { + finalType = tsUnionOf(finalType || 'Record', 'null') + } */ + + if (finalType) { + return finalType + } + + // any type + if (!('type' in schemaObject)) { + return 'unknown' + } + + // if no type could be generated, fall back to “empty object” type + return ctx.emptyObjectsUnknown ? 'Record' : 'Record' +} diff --git a/packages/core/src/helper/schemaObjectToTsType/types.ts b/packages/core/src/helper/schemaObjectToTsType/types.ts new file mode 100644 index 000000000..b06cb992e --- /dev/null +++ b/packages/core/src/helper/schemaObjectToTsType/types.ts @@ -0,0 +1,24 @@ +import type OpenAPIV3 from 'openapi3-ts/oas31' + +/** Context passed to all submodules */ +export interface GlobalContext { + additionalProperties: boolean + alphabetize: boolean + emptyObjectsUnknown: boolean + defaultNonNullable: boolean + discriminators: { [$ref: string]: OpenAPIV3.DiscriminatorObject } + immutableTypes: boolean + indentLv: number + operations: Record< + string, + { + comment?: string + operationType: string + } + > + parameters: Record + pathParamsAsTypes: boolean + silent: boolean + supportArrayLength: boolean + excludeDeprecated: boolean +} diff --git a/packages/core/src/helper/schemaObjectToTsType/utils.ts b/packages/core/src/helper/schemaObjectToTsType/utils.ts new file mode 100644 index 000000000..a312333c0 --- /dev/null +++ b/packages/core/src/helper/schemaObjectToTsType/utils.ts @@ -0,0 +1,324 @@ +import type { SchemaObject } from 'openapi3-ts/oas31' + +const COMMENT_RE = /\*\//g +export const LB_RE = /\r?\n/g +export const DOUBLE_QUOTE_RE = /(?, path: (string | number)[]) => void, + path: (string | number)[] = [], +): void { + if (!obj || typeof obj !== 'object') { + return + } + if (Array.isArray(obj)) { + for (let i = 0; i < obj.length; i++) { + walk(obj[i], cb, path.concat(i)) + } + return + } + // eslint-disable-next-line n/no-callback-literal + cb(obj as any, path) + for (const k of Object.keys(obj)) { + walk((obj as any)[k], cb, path.concat(k)) + } +} + +/** + * Preparing comments from fields + * @see {comment} for output examples + * @returns void if not comments or jsdoc format comment string + */ +export function getSchemaObjectComment(v?: SchemaObject, indentLv?: number): string | undefined { + if (!v || typeof v !== 'object') { + return + } + const output: string[] = [] + + // * Not JSDOC tags: [title, format] + if (v.title) { + output.push(`${v.title} `) + } + if ((v as any).summary) { + output.push(`${(v as any).summary} `) + } + if (v.format) { + output.push(`Format: ${v.format} `) + } + + // * JSDOC tags without value + // 'Deprecated' without value + if (v.deprecated) { + output.push('@deprecated ') + } + + // * JSDOC tags with value + const supportedJsDocTags: (keyof SchemaObject)[] = ['description', 'default', 'example'] + for (const field of supportedJsDocTags) { + const allowEmptyString = field === 'default' || field === 'example' + if (v[field] === undefined) { + continue + } + if (v[field] === '' && !allowEmptyString) { + continue + } + const serialized = typeof v[field] === 'object' ? JSON.stringify(v[field], null, 2) : v[field] + output.push(`@${field} ${serialized} `) + } + + // * JSDOC 'Constant' without value + if ('const' in v) { + output.push('@constant ') + } + + // * JSDOC 'Enum' with type + if (v.enum) { + let type = 'unknown' + if (Array.isArray(v.type)) { + type = v.type.join('|') + } else if (typeof v.type === 'string') { + type = v.type + } + // output.push(`@enum {${type}${v.nullable ? `|null` : ''}}`) + output.push(`@enum {${type}`) + } + + return output.length ? comment(output.join('\n'), indentLv) : undefined +} + +/** wrap any string in a JSDoc-style comment wrapper */ +export function comment(text: string, indentLv?: number): string { + const commentText = text.trim().replace(COMMENT_RE, '*\\/') + + // if single-line comment + if (!commentText.includes('\n')) { + return `/** ${commentText} */` + } + + // if multi-line comment + const ln = indent(' * ', indentLv ?? 0) + return ['/**', `${ln}${commentText.replace(LB_RE, `\n${ln}`)}`, indent(' */', indentLv ?? 0)].join('\n') +} + +/** handle any valid $ref */ +export function parseRef(ref: string): { filename: string; path: string[] } { + if (typeof ref !== 'string') { + return { filename: '.', path: [] } + } + + // OpenAPI $ref + if (ref.includes('#/')) { + const [filename, pathStr] = ref.split('#') + const parts = pathStr.split('/') + const path: string[] = [] + for (const part of parts) { + if (!part || part === 'properties') { + continue + } // remove empty parts and "properties" (gets flattened by TS) + path.push(decodeRef(part)) + } + return { filename: filename || '.', path } + } + if (ref.includes('["')) { + const parts = ref.split('["') + const path: string[] = [] + for (const part of parts) { + const sanitized = part.replace('"]', '').trim() + if (!sanitized || sanitized === 'properties') { + continue + } + path.push(sanitized) + } + return { filename: '.', path } + } + // remote $ref + return { filename: ref, path: [] } +} + +/** Parse TS index */ +export function parseTSIndex(type: string): string[] { + const parts: string[] = [] + const bracketI = type.indexOf('[') + if (bracketI === -1) { + return [type] + } + + parts.push(type.substring(0, bracketI)) + const matches = type.match(TS_INDEX_RE) + if (matches) { + for (const m of matches) { + parts.push(m.substring('["'.length, m.length - '"]'.length)) + } + } + return parts +} + +/** Make TS index */ +export function makeTSIndex(parts: (string | number)[]): string { + return `${parts[0]}[${parts.slice(1).map(escStr).join('][')}]` +} + +/** decode $ref (https://swagger.io/docs/specification/using-ref/#escape) */ +export function decodeRef(ref: string): string { + return ref.replace(ESC_0_RE, '~').replace(ESC_1_RE, '/').replace(DOUBLE_QUOTE_RE, '\\"') +} + +/** encode $ref (https://swagger.io/docs/specification/using-ref/#escape) */ +export function encodeRef(ref: string): string { + return ref.replace(TILDE_RE, '~0').replace(FS_RE, '~1') +} + +/** T[] */ +export function tsArrayOf(type: string): string { + return `(${type})[]` +} + +/** X & Y & Z; */ +export function tsIntersectionOf(...types: string[]): string { + const ts = types.filter(t => t !== 'unknown') + if (ts.length === 0) { + return 'unknown' + } + if (ts.length === 1) { + return String(ts[0]) + } // don’t add parentheses around one thing + return ts.map(t => (TS_UNION_INTERSECTION_RE.test(t) ? `(${t})` : t)).join(' & ') +} + +/** NonNullable */ +export function tsNonNullable(type: string): string { + return `NonNullable<${type}>` +} + +/** OneOf (custom) */ +export function tsOneOf(...types: string[]): string { + if (types.length === 1) { + return types[0] + } + return `OneOf<[${types.join(', ')}]>` +} + +/** Pick */ +export function tsPick(root: string, keys: string[]): string { + return `Pick<${root}, ${tsUnionOf(...keys.map(escStr))}>` +} + +/** Omit */ +export function tsOmit(root: string, keys: string[]): string { + return `Omit<${root}, ${tsUnionOf(...keys.map(escStr))}>` +} + +/** WithRequired */ +export function tsWithRequired(root: string, keys: string[]): string { + return `WithRequired<${root}, ${tsUnionOf(...keys.map(escStr))}>` +} + +/** make a given property key optional */ +export function tsOptionalProperty(key: string): string { + return `${key}?` +} + +/** make a given type readonly */ +export function tsReadonly(type: string): string { + return `readonly ${type}` +} + +/** [T, A, B, ...] */ +export function tsTupleOf(...types: string[]): string { + return `[${types.join(', ')}]` +} + +/** X | Y | Z */ +export function tsUnionOf(...types: (string | number | boolean)[]): string { + if (types.length === 0) { + return 'never' + } + + // de-duplicate the union + const members = new Set() + for (const t of types) { + // unknown swallows everything else in the union + if (t === 'unknown') { + return 'unknown' + } + + members.add(String(t)) + } + + // never gets swallowed by anything else, so only return never + // if it is the only member + if (members.has('never') && members.size === 1) { + return 'never' + } + + // (otherwise remove it) + members.delete('never') + + // return the only member without parentheses + const memberArr = Array.from(members) + if (members.size === 1) { + return memberArr[0] + } + + // build the union string + let out = '' + for (let i = 0; i < memberArr.length; i++) { + const t = memberArr[i] + + // if the type has & or | we should parenthesise it for safety + const escaped = TS_UNION_INTERSECTION_RE.test(t) ? `(${t})` : t + + out += escaped + if (i !== memberArr.length - 1) { + out += ' | ' + } + } + + return out +} + +/** escape string value */ +export function escStr(input: any): string { + if (typeof input !== 'string') { + return input + } + return `"${input.trim().replace(DOUBLE_QUOTE_RE, '\\"')}"` +} + +/** surround a JS object key with quotes, if needed */ +export function escObjKey(input: string): string { + return JS_OBJ_KEY.test(input) ? input : escStr(input) +} + +/** Indent a string */ +export function indent(input: string, level: number) { + if (level > 0) { + return ' '.repeat(level).concat(input) + } + return input +} + +/** call Object.entries() and optionally sort */ +export function getEntries( + obj: ArrayLike | Record, + alphabetize?: boolean, + excludeDeprecated?: boolean, +) { + let entries = Object.entries(obj) + if (alphabetize) { + entries.sort(([a], [b]) => a.localeCompare(b, 'en', { numeric: true })) + } + if (excludeDeprecated) { + entries = entries.filter(([, v]) => !(v && typeof v === 'object' && 'deprecated' in v && v.deprecated)) + } + return entries +} diff --git a/packages/core/src/helper/string/convertToCamelCase.impl.ts b/packages/core/src/helper/string/convertToCamelCase.impl.ts index b4e25f935..0e595e136 100644 --- a/packages/core/src/helper/string/convertToCamelCase.impl.ts +++ b/packages/core/src/helper/string/convertToCamelCase.impl.ts @@ -7,10 +7,10 @@ * @group Helper */ export const convertToCamelCase = (str: string): string => { - const s = - str - .match(/[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g) - ?.map((x) => x.slice(0, 1).toUpperCase() + x.slice(1).toLowerCase()) - .join('') ?? str - return s.slice(0, 1).toLowerCase() + s.slice(1) + const s = + str + .match(/[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g) + ?.map(x => x.slice(0, 1).toUpperCase() + x.slice(1).toLowerCase()) + .join('') ?? str + return s.slice(0, 1).toLowerCase() + s.slice(1) } diff --git a/packages/core/src/helper/string/convertToCamelCase.test.ts b/packages/core/src/helper/string/convertToCamelCase.test.ts index 76dc28b87..6b13339dd 100644 --- a/packages/core/src/helper/string/convertToCamelCase.test.ts +++ b/packages/core/src/helper/string/convertToCamelCase.test.ts @@ -1,23 +1,23 @@ import { convertToCamelCase } from './convertToCamelCase.impl.js' describe('convertToCamelCase', () => { - it('converts from snakeCase', () => { - const result = convertToCamelCase('snake-case-text') - expect('snakeCaseText').toBe(result) - }) + it('converts from snakeCase', () => { + const result = convertToCamelCase('snake-case-text') + expect('snakeCaseText').toBe(result) + }) - it('converts from pascal case', () => { - const result = convertToCamelCase('PascalCaseText') - expect('pascalCaseText').toBe(result) - }) + it('converts from pascal case', () => { + const result = convertToCamelCase('PascalCaseText') + expect('pascalCaseText').toBe(result) + }) - it('converts from kebab case', () => { - const result = convertToCamelCase('kebab-case-text') - expect('kebabCaseText').toBe(result) - }) + it('converts from kebab case', () => { + const result = convertToCamelCase('kebab-case-text') + expect('kebabCaseText').toBe(result) + }) - it('converts from mixed text', () => { - const result = convertToCamelCase('some-mixed_string _With spaces_underscores-and-hyphens') - expect('someMixedStringWithSpacesUnderscoresAndHyphens').toBe(result) - }) + it('converts from mixed text', () => { + const result = convertToCamelCase('some-mixed_string _With spaces_underscores-and-hyphens') + expect('someMixedStringWithSpacesUnderscoresAndHyphens').toBe(result) + }) }) diff --git a/packages/core/src/helper/string/convertToKebabCase.impl.ts b/packages/core/src/helper/string/convertToKebabCase.impl.ts index 383ee3fac..05724b5dd 100644 --- a/packages/core/src/helper/string/convertToKebabCase.impl.ts +++ b/packages/core/src/helper/string/convertToKebabCase.impl.ts @@ -7,7 +7,7 @@ * @group Helper */ export const convertToKebabCase = (str: string): string => - str - .match(/[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g) - ?.map((x) => x.toLowerCase()) - .join('-') ?? str + str + .match(/[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g) + ?.map(x => x.toLowerCase()) + .join('-') ?? str diff --git a/packages/core/src/helper/string/convertToKebabCase.test.ts b/packages/core/src/helper/string/convertToKebabCase.test.ts index 80dc879e1..12a788691 100644 --- a/packages/core/src/helper/string/convertToKebabCase.test.ts +++ b/packages/core/src/helper/string/convertToKebabCase.test.ts @@ -1,23 +1,23 @@ import { convertToKebabCase } from './convertToKebabCase.impl.js' describe('convertToKebabCase', () => { - it('converts from snakeCase', () => { - const result = convertToKebabCase('snake-case-text') - expect('snake-case-text').toBe(result) - }) + it('converts from snakeCase', () => { + const result = convertToKebabCase('snake-case-text') + expect('snake-case-text').toBe(result) + }) - it('converts from pascal case', () => { - const result = convertToKebabCase('PascalCaseText') - expect('pascal-case-text').toBe(result) - }) + it('converts from pascal case', () => { + const result = convertToKebabCase('PascalCaseText') + expect('pascal-case-text').toBe(result) + }) - it('converts from camelcase', () => { - const result = convertToKebabCase('camelCaseText') - expect('camel-case-text').toBe(result) - }) + it('converts from camelcase', () => { + const result = convertToKebabCase('camelCaseText') + expect('camel-case-text').toBe(result) + }) - it('converts from mixed text', () => { - const result = convertToKebabCase('some-mixed_string _With spaces_underscores-and-hyphens') - expect('some-mixed-string-with-spaces-underscores-and-hyphens').toBe(result) - }) + it('converts from mixed text', () => { + const result = convertToKebabCase('some-mixed_string _With spaces_underscores-and-hyphens') + expect('some-mixed-string-with-spaces-underscores-and-hyphens').toBe(result) + }) }) diff --git a/packages/core/src/helper/string/convertToPascalCase.impl.ts b/packages/core/src/helper/string/convertToPascalCase.impl.ts index 9a6055fa5..3e1acf696 100644 --- a/packages/core/src/helper/string/convertToPascalCase.impl.ts +++ b/packages/core/src/helper/string/convertToPascalCase.impl.ts @@ -7,7 +7,7 @@ * @group Helper */ export const convertToPascalCase = (str: string): string => - str - .match(/[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g) - ?.map((x) => x.charAt(0).toUpperCase() + x.slice(1).toLowerCase()) - .join('') ?? str + str + .match(/[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g) + ?.map(x => x.charAt(0).toUpperCase() + x.slice(1).toLowerCase()) + .join('') ?? str diff --git a/packages/core/src/helper/string/convertToPascalCase.test.ts b/packages/core/src/helper/string/convertToPascalCase.test.ts index d9e757c48..0af793d1f 100644 --- a/packages/core/src/helper/string/convertToPascalCase.test.ts +++ b/packages/core/src/helper/string/convertToPascalCase.test.ts @@ -1,23 +1,23 @@ import { convertToPascalCase } from './convertToPascalCase.impl.js' describe('convertToPascalCase', () => { - it('converts from snakeCase', () => { - const result = convertToPascalCase('snake-case-text') - expect('SnakeCaseText').toBe(result) - }) + it('converts from snakeCase', () => { + const result = convertToPascalCase('snake-case-text') + expect('SnakeCaseText').toBe(result) + }) - it('converts from camel case', () => { - const result = convertToPascalCase('camelCaseText') - expect('CamelCaseText').toBe(result) - }) + it('converts from camel case', () => { + const result = convertToPascalCase('camelCaseText') + expect('CamelCaseText').toBe(result) + }) - it('converts from kebab case', () => { - const result = convertToPascalCase('kebab-case-text') - expect('KebabCaseText').toBe(result) - }) + it('converts from kebab case', () => { + const result = convertToPascalCase('kebab-case-text') + expect('KebabCaseText').toBe(result) + }) - it('converts from mixed text', () => { - const result = convertToPascalCase('some-mixed_string _With spaces_underscores-and-hyphens') - expect('SomeMixedStringWithSpacesUnderscoresAndHyphens').toBe(result) - }) + it('converts from mixed text', () => { + const result = convertToPascalCase('some-mixed_string _With spaces_underscores-and-hyphens') + expect('SomeMixedStringWithSpacesUnderscoresAndHyphens').toBe(result) + }) }) diff --git a/packages/core/src/helper/string/convertToSnakeCase.impl.ts b/packages/core/src/helper/string/convertToSnakeCase.impl.ts index 33da530fb..1d8e8c757 100644 --- a/packages/core/src/helper/string/convertToSnakeCase.impl.ts +++ b/packages/core/src/helper/string/convertToSnakeCase.impl.ts @@ -7,7 +7,7 @@ * @group Helper */ export const convertToSnakeCase = (str: string): string => - str - .match(/[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g) - ?.map((x) => x.toLowerCase()) - .join('_') ?? str + str + .match(/[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g) + ?.map(x => x.toLowerCase()) + .join('_') ?? str diff --git a/packages/core/src/helper/string/convertToSnakeCase.test.ts b/packages/core/src/helper/string/convertToSnakeCase.test.ts index da1eaee6d..4c93b662d 100644 --- a/packages/core/src/helper/string/convertToSnakeCase.test.ts +++ b/packages/core/src/helper/string/convertToSnakeCase.test.ts @@ -1,23 +1,23 @@ import { convertToSnakeCase } from './convertToSnakeCase.impl.js' describe('convertToSnakeCase', () => { - it('converts from camelCase', () => { - const result = convertToSnakeCase('camelCaseText') - expect('camel_case_text').toBe(result) - }) + it('converts from camelCase', () => { + const result = convertToSnakeCase('camelCaseText') + expect('camel_case_text').toBe(result) + }) - it('converts from pascal case', () => { - const result = convertToSnakeCase('PascalCaseText') - expect('pascal_case_text').toBe(result) - }) + it('converts from pascal case', () => { + const result = convertToSnakeCase('PascalCaseText') + expect('pascal_case_text').toBe(result) + }) - it('converts from kebab case', () => { - const result = convertToSnakeCase('kebab-case-text') - expect('kebab_case_text').toBe(result) - }) + it('converts from kebab case', () => { + const result = convertToSnakeCase('kebab-case-text') + expect('kebab_case_text').toBe(result) + }) - it('converts from mixed text', () => { - const result = convertToSnakeCase('some-mixed_string _With spaces_underscores-and-hyphens') - expect('some_mixed_string_with_spaces_underscores_and_hyphens').toBe(result) - }) + it('converts from mixed text', () => { + const result = convertToSnakeCase('some-mixed_string _With spaces_underscores-and-hyphens') + expect('some_mixed_string_with_spaces_underscores_and_hyphens').toBe(result) + }) }) diff --git a/packages/core/src/helper/throwIfNotValidMessage.impl.ts b/packages/core/src/helper/throwIfNotValidMessage.impl.ts index 737144aff..92d34b97b 100644 --- a/packages/core/src/helper/throwIfNotValidMessage.impl.ts +++ b/packages/core/src/helper/throwIfNotValidMessage.impl.ts @@ -8,34 +8,34 @@ import { EBMessageType, StatusCode, UnhandledError } from '../core/index.js' * @param input */ export const throwIfNotValidMessage = (input: unknown) => { - const ebMessageSchema = z - .object({ - messageType: z.nativeEnum(EBMessageType), - id: z.string(), - sender: z.object({ - serviceName: z.string(), - serviceVersion: z.string(), - serviceTarget: z.string(), - instanceId: z.string(), - }), - timestamp: z.number(), - contentType: z.string(), - contentEncoding: z.string(), - traceId: z.string().optional(), - correlationId: z.string().optional(), - principalId: z.string().optional(), - tenantId: z.string().optional(), - eventName: z.string().optional(), - otp: z.string().optional(), - }) - .passthrough() + const ebMessageSchema = z + .object({ + messageType: z.nativeEnum(EBMessageType), + id: z.string(), + sender: z.object({ + serviceName: z.string(), + serviceVersion: z.string(), + serviceTarget: z.string(), + instanceId: z.string(), + }), + timestamp: z.number(), + contentType: z.string(), + contentEncoding: z.string(), + traceId: z.string().optional(), + correlationId: z.string().optional(), + principalId: z.string().optional(), + tenantId: z.string().optional(), + eventName: z.string().optional(), + otp: z.string().optional(), + }) + .passthrough() - try { - ebMessageSchema.parse(input) - } catch (error) { - throw new UnhandledError( - StatusCode.BadRequest, - 'Input is no valid PURISTA event bridge message - see https://purista.dev', - ) - } + try { + ebMessageSchema.parse(input) + } catch (error) { + throw new UnhandledError( + StatusCode.BadRequest, + 'Input is no valid PURISTA event bridge message - see https://purista.dev', + ) + } } diff --git a/packages/core/src/helper/throwIfNotValidMessage.test.ts b/packages/core/src/helper/throwIfNotValidMessage.test.ts index 2617b2a35..4c7fcc285 100644 --- a/packages/core/src/helper/throwIfNotValidMessage.test.ts +++ b/packages/core/src/helper/throwIfNotValidMessage.test.ts @@ -2,38 +2,38 @@ import { StatusCode, UnhandledError } from '../core/index.js' import { throwIfNotValidMessage } from './throwIfNotValidMessage.impl.js' describe('throwIfNotValidMessage', () => { - test("valid input doesn't throw", () => { - expect(() => { - throwIfNotValidMessage({ - sender: { - serviceName: 'SenderService', - serviceVersion: '1', - serviceTarget: 'senderServiceTarget', - instanceId: 'SenderServiceInstance', - }, - messageType: 'command', - id: 'my-id', - timestamp: 123456, - contentType: 'application/json', - contentEncoding: 'utf-8', - traceId: 'my-trace-id', - correlationId: 'my-correlation-id', - principalId: 'my-principal-id', - tenantId: 'my-tenant-id', - eventName: 'my-event-name', - otp: 'my-otp', - }) - }).not.toThrow() - }) + test("valid input doesn't throw", () => { + expect(() => { + throwIfNotValidMessage({ + sender: { + serviceName: 'SenderService', + serviceVersion: '1', + serviceTarget: 'senderServiceTarget', + instanceId: 'SenderServiceInstance', + }, + messageType: 'command', + id: 'my-id', + timestamp: 123456, + contentType: 'application/json', + contentEncoding: 'utf-8', + traceId: 'my-trace-id', + correlationId: 'my-correlation-id', + principalId: 'my-principal-id', + tenantId: 'my-tenant-id', + eventName: 'my-event-name', + otp: 'my-otp', + }) + }).not.toThrow() + }) - test('invalid input throws an error', () => { - expect(() => { - throwIfNotValidMessage({ invalid: 'input' }) - }).toThrowError( - new UnhandledError( - StatusCode.BadRequest, - 'Input is no valid PURISTA event bridge message - see https://purista.dev', - ), - ) - }) + test('invalid input throws an error', () => { + expect(() => { + throwIfNotValidMessage({ invalid: 'input' }) + }).toThrowError( + new UnhandledError( + StatusCode.BadRequest, + 'Input is no valid PURISTA event bridge message - see https://purista.dev', + ), + ) + }) }) diff --git a/packages/core/src/helper/types/Constructor.ts b/packages/core/src/helper/types/Constructor.ts new file mode 100644 index 000000000..382c67702 --- /dev/null +++ b/packages/core/src/helper/types/Constructor.ts @@ -0,0 +1 @@ +export type Constructor = new (...args: any[]) => T diff --git a/packages/core/src/helper/types/FullDefinition.ts b/packages/core/src/helper/types/FullDefinition.ts new file mode 100644 index 000000000..9e750b869 --- /dev/null +++ b/packages/core/src/helper/types/FullDefinition.ts @@ -0,0 +1,10 @@ +import type { FullServiceDefinition } from './FullServiceDefinition.js' + +export type FullDefinition = { + version: string + rest?: { + apiPath: string + domain: string + } + services: FullServiceDefinition +} diff --git a/packages/core/src/helper/types/FullServiceDefinition.ts b/packages/core/src/helper/types/FullServiceDefinition.ts new file mode 100644 index 000000000..841adfd33 --- /dev/null +++ b/packages/core/src/helper/types/FullServiceDefinition.ts @@ -0,0 +1,16 @@ +import type { CommandDefinition, Service, SubscriptionDefinition } from '../../core/index.js' + +export type FullServiceDefinition = { + [serviceName: string]: { + [serviceVersion: string]: { + description: string + deprecated: boolean + commands: { + [commandName: string]: CommandDefinition + } + subscriptions: { + [subscriptionName: string]: SubscriptionDefinition + } + } + } +} diff --git a/packages/core/src/helper/types/InstanceOrType.ts b/packages/core/src/helper/types/InstanceOrType.ts new file mode 100644 index 000000000..a25bcefb5 --- /dev/null +++ b/packages/core/src/helper/types/InstanceOrType.ts @@ -0,0 +1,3 @@ +import type { Constructor } from './Constructor.js' + +export type InstanceOrType = T extends Constructor ? InstanceType : T diff --git a/packages/core/src/helper/types/IsConstructor.ts b/packages/core/src/helper/types/IsConstructor.ts new file mode 100644 index 000000000..0237bb348 --- /dev/null +++ b/packages/core/src/helper/types/IsConstructor.ts @@ -0,0 +1,3 @@ +import type { Constructor } from './Constructor.js' + +export type IsConstructor = T extends Constructor ? true : false diff --git a/packages/core/src/helper/types/ObjectWithKeysFromStringArray.ts b/packages/core/src/helper/types/ObjectWithKeysFromStringArray.ts index be7987cf9..2b105e341 100644 --- a/packages/core/src/helper/types/ObjectWithKeysFromStringArray.ts +++ b/packages/core/src/helper/types/ObjectWithKeysFromStringArray.ts @@ -3,5 +3,5 @@ * */ export type ObjectWithKeysFromStringArray, Value = unknown | undefined> = { - [K in T extends ReadonlyArray ? U : never]: Value + [K in T extends ReadonlyArray ? U : never]: Value } diff --git a/packages/core/src/helper/types/ServiceDefinitions.ts b/packages/core/src/helper/types/ServiceDefinitions.ts new file mode 100644 index 000000000..6678a9863 --- /dev/null +++ b/packages/core/src/helper/types/ServiceDefinitions.ts @@ -0,0 +1,10 @@ +import type { CommandDefinitionListResolved, SubscriptionDefinitionListResolved } from '../../core/index.js' + +export type ServiceDefinitions = { + commands: CommandDefinitionListResolved + subscriptions: SubscriptionDefinitionListResolved + serviceName: string + serviceVersion: string + serviceDescription: string + deprecated: boolean +} diff --git a/packages/core/src/helper/types/ShutdownEntry.ts b/packages/core/src/helper/types/ShutdownEntry.ts index b42f9173a..3067c1722 100644 --- a/packages/core/src/helper/types/ShutdownEntry.ts +++ b/packages/core/src/helper/types/ShutdownEntry.ts @@ -2,8 +2,8 @@ * Entry of thing you like to shutdown gracefully */ export type ShutdownEntry = { - /** the name */ - name: string - /** a async function that is called during shutdown */ - destroy: () => Promise + /** the name */ + name: string + /** a async function that is called during shutdown */ + destroy: () => Promise } diff --git a/packages/core/src/helper/types/index.ts b/packages/core/src/helper/types/index.ts index 67b31b3de..21e967b12 100644 --- a/packages/core/src/helper/types/index.ts +++ b/packages/core/src/helper/types/index.ts @@ -1,3 +1,9 @@ +export * from './FullDefinition.js' +export * from './FullServiceDefinition.js' export * from './NonEmptyString.js' export * from './ObjectWithKeysFromStringArray.js' +export * from './ServiceDefinitions.js' export * from './ShutdownEntry.js' +export * from './Constructor.js' +export * from './IsConstructor.js' +export * from './InstanceOrType.js' diff --git a/packages/core/src/index.test.ts b/packages/core/src/index.test.ts index 7a27f4dad..7b14f23e8 100644 --- a/packages/core/src/index.test.ts +++ b/packages/core/src/index.test.ts @@ -1,107 +1,110 @@ import { - CommandDefinitionBuilder, - convertToCamelCase, - convertToKebabCase, - convertToPascalCase, - convertToSnakeCase, - createErrorResponse, - createInfoMessage, - createSuccessResponse, - DefaultConfigStore, - DefaultEventBridge, - DefaultLogger, - DefaultSecretStore, - DefaultStateStore, - EBMessageType, - getCleanedMessage, - getCommandContextMock, - getCommandErrorMessageMock, - getCommandFunctionWithValidation, - getCommandMessageMock, - getCommandSuccessMessageMock, - getCustomMessageMessageMock, - getErrorMessageForCode, - getEventBridgeMock, - getLoggerMock, - getNewCorrelationId, - getNewEBMessageId, - getNewTraceId, - getSubscriptionContextMock, - getSubscriptionFunctionWithValidation, - getUniqueId, - HandledError, - HttpClient, - infoMessageTypes, - initLogger, - isCommand, - isCommandErrorResponse, - isCommandResponse, - isCommandSuccessResponse, - isInfoMessage, - isInfoServiceFunctionAdded, - puristaVersion, - Service, - ServiceBuilder, - StatusCode, - SubscriptionDefinitionBuilder, - UnhandledError, + ClientBuilder, + CommandDefinitionBuilder, + DefaultConfigStore, + DefaultEventBridge, + DefaultLogger, + DefaultSecretStore, + DefaultStateStore, + EBMessageType, + HandledError, + HttpClient, + Service, + ServiceBuilder, + StatusCode, + SubscriptionDefinitionBuilder, + UnhandledError, + convertToCamelCase, + convertToKebabCase, + convertToPascalCase, + convertToSnakeCase, + createErrorResponse, + createInfoMessage, + createSuccessResponse, + getCleanedMessage, + getCommandContextMock, + getCommandErrorMessageMock, + getCommandMessageMock, + getCommandSuccessMessageMock, + getCustomMessageMessageMock, + getErrorMessageForCode, + getEventBridgeMock, + getLoggerMock, + getNewCorrelationId, + getNewEBMessageId, + getNewTraceId, + getSubscriptionContextMock, + getSubscriptionFunctionWithValidation, + getUniqueId, + infoMessageTypes, + initLogger, + isCommand, + isCommandErrorResponse, + isCommandResponse, + isCommandSuccessResponse, + isInfoMessage, + isInfoServiceFunctionAdded, + puristaVersion, + schemaObjectToTsType, } from './index.js' it('exports core functions', () => { - expect(puristaVersion).toBeDefined() - // core - expect(DefaultEventBridge).toBeDefined() - expect(DefaultLogger).toBeDefined() - expect(createErrorResponse).toBeDefined() - expect(createInfoMessage).toBeDefined() - expect(createSuccessResponse).toBeDefined() - expect(getCleanedMessage).toBeDefined() - expect(getErrorMessageForCode).toBeDefined() - expect(getNewCorrelationId).toBeDefined() - expect(getNewEBMessageId).toBeDefined() - expect(getNewTraceId).toBeDefined() - expect(getUniqueId).toBeDefined() - expect(infoMessageTypes).toBeDefined() - expect(initLogger).toBeDefined() - expect(isCommand).toBeDefined() - expect(isCommandErrorResponse).toBeDefined() - expect(isCommandResponse).toBeDefined() - expect(isCommandSuccessResponse).toBeDefined() - expect(isInfoMessage).toBeDefined() - expect(isInfoServiceFunctionAdded).toBeDefined() - expect(HandledError).toBeDefined() - expect(EBMessageType).toBeDefined() - expect(Service).toBeDefined() - expect(StatusCode).toBeDefined() - expect(UnhandledError).toBeDefined() + expect(puristaVersion).toBeDefined() + // core + expect(DefaultEventBridge).toBeDefined() + expect(DefaultLogger).toBeDefined() + expect(createErrorResponse).toBeDefined() + expect(createInfoMessage).toBeDefined() + expect(createSuccessResponse).toBeDefined() + expect(getCleanedMessage).toBeDefined() + expect(getErrorMessageForCode).toBeDefined() + expect(getNewCorrelationId).toBeDefined() + expect(getNewEBMessageId).toBeDefined() + expect(getNewTraceId).toBeDefined() + expect(getUniqueId).toBeDefined() + expect(infoMessageTypes).toBeDefined() + expect(initLogger).toBeDefined() + expect(isCommand).toBeDefined() + expect(isCommandErrorResponse).toBeDefined() + expect(isCommandResponse).toBeDefined() + expect(isCommandSuccessResponse).toBeDefined() + expect(isInfoMessage).toBeDefined() + expect(isInfoServiceFunctionAdded).toBeDefined() + expect(HandledError).toBeDefined() + expect(EBMessageType).toBeDefined() + expect(Service).toBeDefined() + expect(StatusCode).toBeDefined() + expect(UnhandledError).toBeDefined() - // stores - expect(DefaultConfigStore).toBeDefined() - expect(DefaultSecretStore).toBeDefined() - expect(DefaultStateStore).toBeDefined() + // stores + expect(DefaultConfigStore).toBeDefined() + expect(DefaultSecretStore).toBeDefined() + expect(DefaultStateStore).toBeDefined() - // http client - expect(HttpClient).toBeDefined() + // http client + expect(HttpClient).toBeDefined() - // helper - expect(ServiceBuilder).toBeDefined() - expect(CommandDefinitionBuilder).toBeDefined() - expect(getCommandFunctionWithValidation).toBeDefined() - expect(getSubscriptionFunctionWithValidation).toBeDefined() - expect(SubscriptionDefinitionBuilder).toBeDefined() - expect(convertToCamelCase).toBeDefined() - expect(convertToKebabCase).toBeDefined() - expect(convertToPascalCase).toBeDefined() - expect(convertToSnakeCase).toBeDefined() + // helper + expect(ServiceBuilder).toBeDefined() + expect(CommandDefinitionBuilder).toBeDefined() + expect(getSubscriptionFunctionWithValidation).toBeDefined() + expect(SubscriptionDefinitionBuilder).toBeDefined() + expect(convertToCamelCase).toBeDefined() + expect(convertToKebabCase).toBeDefined() + expect(convertToPascalCase).toBeDefined() + expect(convertToSnakeCase).toBeDefined() - // test helper - expect(getCommandContextMock).toBeDefined() - expect(getSubscriptionContextMock).toBeDefined() - expect(getLoggerMock).toBeDefined() - expect(getCommandContextMock).toBeDefined() - expect(getEventBridgeMock).toBeDefined() - expect(getCommandErrorMessageMock).toBeDefined() - expect(getCommandMessageMock).toBeDefined() - expect(getCommandSuccessMessageMock).toBeDefined() - expect(getCustomMessageMessageMock).toBeDefined() + // test helper + expect(getCommandContextMock).toBeDefined() + expect(getSubscriptionContextMock).toBeDefined() + expect(getLoggerMock).toBeDefined() + expect(getCommandContextMock).toBeDefined() + expect(getEventBridgeMock).toBeDefined() + expect(getCommandErrorMessageMock).toBeDefined() + expect(getCommandMessageMock).toBeDefined() + expect(getCommandSuccessMessageMock).toBeDefined() + expect(getCustomMessageMessageMock).toBeDefined() + + expect(ClientBuilder).toBeDefined() + expect(schemaObjectToTsType).toBeDefined() }) diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 8fac8dc7d..352797496 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -29,14 +29,15 @@ export * from './DefaultStateStore/index.js' export * from './ServiceBuilder/index.js' export * from './mocks/index.js' export * from './zodOpenApi/index.js' +export * from './ClientBuilder/index.js' declare global { - interface FetchEvent extends Event { - readonly request: Request - respondWith(response: Promise | Response): Promise - } - interface ExecutionContext { - waitUntil(promise: Promise): void - passThroughOnException(): void - } + interface FetchEvent extends Event { + readonly request: Request + respondWith(response: Promise | Response): Promise + } + interface ExecutionContext { + waitUntil(promise: Promise): void + passThroughOnException(): void + } } diff --git a/packages/core/src/mocks/getCommandContext.mock.ts b/packages/core/src/mocks/getCommandContext.mock.ts index 317985433..b26396b73 100644 --- a/packages/core/src/mocks/getCommandContext.mock.ts +++ b/packages/core/src/mocks/getCommandContext.mock.ts @@ -3,10 +3,11 @@ import type { SinonSandbox, SinonStub } from 'sinon' import { stub } from 'sinon' import type { - CommandFunctionContext, - EBMessageAddress, - FromEmitToOtherType, - FromInvokeToOtherType, + CommandFunctionContext, + EBMessageAddress, + FromEmitToOtherType, + FromInvokeToOtherType, + InvokeList, } from '../core/index.js' import { getLoggerMock } from './getLogger.mock.js' import { getCommandMessageMock } from './messages/index.js' @@ -17,165 +18,196 @@ import { getCommandMessageMock } from './messages/index.js' * @group Unit test helper * */ export const getCommandContextMock = < - MessagePayloadType = unknown, - MessageParamsType = unknown, - Invokes = {}, - EmitListType = {}, ->( - payload: MessagePayloadType, - parameter: MessageParamsType, - sandbox?: SinonSandbox, - _invokes?: FromInvokeToOtherType< - Invokes, - { outputSchema?: Schema; payloadSchema?: Schema; parameterSchema?: Schema } - >, - emitList?: EmitListType, -) => { - const logger = getLoggerMock(sandbox) - - const getMockSpan = () => { - return { - spanContext: () => { - return { - traceId: 'fake', - spanId: 'fake', - isRemote: false, - traceFlags: 0, - } - }, - setAttribute: sandbox?.stub() ?? stub(), - setAttributes: sandbox?.stub() ?? stub(), - addEvent: sandbox?.stub() ?? stub(), - setStatus: sandbox?.stub() ?? stub(), - updateName: sandbox?.stub() ?? stub(), - end: sandbox?.stub() ?? stub(), - isRecording: () => true, - recordException: (sandbox?.stub() ?? stub()).callsFake((err: any) => { - // eslint-disable-next-line no-console - console.error(err) - }), - } - } - - const invokeMocks: Record>> = {} - - const getInvokeProxy = (address?: EBMessageAddress, lvl = 0): TFaux => { - const adr = { - serviceName: '', - serviceTarget: '', - serviceVersion: '', - ...address, - } - - return new Proxy(() => {}, { - get(obj: Record, name) { - if (typeof name !== 'string' || name === 'then' || name === 'catch' || name === 'finally') { - return undefined - } - - const x = obj[name] - if (lvl === 0) { - const na = { - ...adr, - serviceName: name, - } - if (!invokeMocks[na.serviceName]) { - invokeMocks[na.serviceName] = {} - } - return getInvokeProxy(na, lvl + 1) - } - if (lvl === 1) { - const na = { - ...adr, - serviceVersion: name, - } - if (!invokeMocks[na.serviceName][na.serviceVersion]) { - invokeMocks[na.serviceName][na.serviceVersion] = {} - } - return getInvokeProxy(na, lvl + 1) - } - - if (lvl === 2) { - const na = { - ...adr, - serviceTarget: name, - } - if (!invokeMocks[na.serviceName][na.serviceVersion][na.serviceTarget]) { - invokeMocks[na.serviceName][na.serviceVersion][na.serviceTarget] = sandbox?.stub() ?? stub() - - invokeMocks[na.serviceName][na.serviceVersion][na.serviceTarget].rejects( - new Error( - `invocation of ${na.serviceTarget} in service ${na.serviceName} version ${na.serviceVersion} is not stubbed`, - ), - ) - } - return invokeMocks[na.serviceName]?.[na.serviceVersion]?.[na.serviceTarget] - } - }, - }) as TFaux - } - - const eventList = Object.keys(emitList ?? {}).reduce((prev, current) => { - return { - ...prev, - [current]: sandbox?.stub() ?? stub().resolves(), - } - }, {}) as FromEmitToOtherType - - const stubs = { - logger: logger.stubs, - emit: eventList, - invoke: sandbox?.stub() ?? stub(), - wrapInSpan: sandbox?.stub() ?? stub(), - startActiveSpan: sandbox?.stub() ?? stub(), - getSecret: sandbox?.stub() ?? stub(), - setSecret: sandbox?.stub() ?? stub(), - removeSecret: sandbox?.stub() ?? stub(), - getConfig: sandbox?.stub() ?? stub(), - setConfig: sandbox?.stub() ?? stub(), - removeConfig: sandbox?.stub() ?? stub(), - getState: sandbox?.stub() ?? stub(), - setState: sandbox?.stub() ?? stub(), - removeState: sandbox?.stub() ?? stub(), - service: getInvokeProxy>(), - } - - const message = getCommandMessageMock({ - payload: { - payload, - parameter, - }, - }) - - const mock: CommandFunctionContext = { - logger: logger.mock, - message, - emit: async (eventName: K, payload: Payload) => { - return eventList[eventName](eventName, payload) - }, - invoke: stubs.invoke.rejects(new Error('Invoke is not stubbed')), - wrapInSpan: stubs.wrapInSpan.callsFake((_name, _opts, fn) => fn(getMockSpan())), - startActiveSpan: stubs.startActiveSpan.callsFake((_name, _opts, _context, fn) => fn(getMockSpan())), - service: getInvokeProxy(), - secrets: { - getSecret: stubs.getSecret.rejects(new Error('getSecret is not stubbed')), - setSecret: stubs.setSecret.rejects(new Error('setSecret is not stubbed')), - removeSecret: stubs.removeSecret.rejects(new Error('removeSecret is not stubbed')), - }, - configs: { - getConfig: stubs.getConfig.rejects(new Error('getConfig is not stubbed')), - setConfig: stubs.setConfig.rejects(new Error('setConfig is not stubbed')), - removeConfig: stubs.removeConfig.rejects(new Error('removeConfig is not stubbed')), - }, - states: { - getState: stubs.getState.rejects(new Error('getState is not stubbed')), - setState: stubs.setState.rejects(new Error('setState is not stubbed')), - removeState: stubs.removeState.rejects(new Error('removeState is not stubbed')), - }, - } - - return { - mock, - stubs, - } + MessagePayloadType, + MessageParamsType, + FunctionPayloadType, + FunctionParamsType, + Resources extends Record, + Invokes extends InvokeList, + EmitList extends Record, +>(input: { + payload: FunctionPayloadType + parameter: FunctionParamsType + sandbox?: SinonSandbox + invokes: FromInvokeToOtherType + emitList: FromEmitToOtherType + resources?: Partial + message?: { + payload: MessagePayloadType + parameter: MessageParamsType + } +}) => { + const logger = getLoggerMock(input.sandbox) + + const getMockSpan = () => { + return { + spanContext: () => { + return { + traceId: 'fake', + spanId: 'fake', + isRemote: false, + traceFlags: 0, + } + }, + setAttribute: input.sandbox?.stub() ?? stub(), + setAttributes: input.sandbox?.stub() ?? stub(), + addEvent: input.sandbox?.stub() ?? stub(), + setStatus: input.sandbox?.stub() ?? stub(), + updateName: input.sandbox?.stub() ?? stub(), + end: input.sandbox?.stub() ?? stub(), + isRecording: () => true, + recordException: (input.sandbox?.stub() ?? stub()).callsFake((err: any) => { + // biome-ignore lint/suspicious/noConsole: no logger available + console.error(err) + }), + } + } + + const providedResources: Partial = input.resources ?? ({} as Partial) + + const resourcesProxy = new Proxy( + {}, + { + get(obj: Record, name) { + if (typeof name !== 'string' || name === 'then' || name === 'catch' || name === 'finally') { + throw new Error('Invalid property access on resources proxy') + } + if (Object.hasOwn(providedResources, name)) { + return providedResources[name] + } + if (!Object.hasOwn(stubs.resource, name)) { + throw new Error(`Resource ${name} not set or stubbed`) + } + return stubs.resource[name] + }, + }, + ) as Resources + + const invokeMocks: Record>> = {} + + const getInvokeProxy = (address?: EBMessageAddress, lvl = 0): TFaux => { + const adr = { + serviceName: '', + serviceTarget: '', + serviceVersion: '', + ...address, + } + + return new Proxy(() => {}, { + get(obj: Record, name) { + if (typeof name !== 'string' || name === 'then' || name === 'catch' || name === 'finally') { + return undefined + } + + const x = obj[name] + if (lvl === 0) { + const na = { + ...adr, + serviceName: name, + } + if (!invokeMocks[na.serviceName]) { + invokeMocks[na.serviceName] = {} + } + return getInvokeProxy(na, lvl + 1) + } + if (lvl === 1) { + const na = { + ...adr, + serviceVersion: name, + } + if (!invokeMocks[na.serviceName][na.serviceVersion]) { + invokeMocks[na.serviceName][na.serviceVersion] = {} + } + return getInvokeProxy(na, lvl + 1) + } + + if (lvl === 2) { + const na = { + ...adr, + serviceTarget: name, + } + if (!invokeMocks[na.serviceName][na.serviceVersion][na.serviceTarget]) { + invokeMocks[na.serviceName][na.serviceVersion][na.serviceTarget] = input.sandbox?.stub() ?? stub() + + invokeMocks[na.serviceName][na.serviceVersion][na.serviceTarget].rejects( + new Error( + `invocation of ${na.serviceTarget} in service ${na.serviceName} version ${na.serviceVersion} is not stubbed`, + ), + ) + } + return invokeMocks[na.serviceName]?.[na.serviceVersion]?.[na.serviceTarget] + } + }, + }) as TFaux + } + + const eventList = Object.keys(input.emitList ?? {}).reduce((prev, current) => { + return { + // biome-ignore lint/performance/noAccumulatingSpread: + ...prev, + [current]: input.sandbox?.stub() ?? stub().resolves(), + } + }, {}) as FromEmitToOtherType + + const stubs = { + logger: logger.stubs, + emit: eventList, + invoke: input.sandbox?.stub() ?? stub(), + wrapInSpan: input.sandbox?.stub() ?? stub(), + startActiveSpan: input.sandbox?.stub() ?? stub(), + getSecret: input.sandbox?.stub() ?? stub(), + setSecret: input.sandbox?.stub() ?? stub(), + removeSecret: input.sandbox?.stub() ?? stub(), + getConfig: input.sandbox?.stub() ?? stub(), + setConfig: input.sandbox?.stub() ?? stub(), + removeConfig: input.sandbox?.stub() ?? stub(), + getState: input.sandbox?.stub() ?? stub(), + setState: input.sandbox?.stub() ?? stub(), + removeState: input.sandbox?.stub() ?? stub(), + service: getInvokeProxy>(), + resource: {} as Partial, + } + + const message = getCommandMessageMock( + input.message + ? { payload: input.message } + : { + payload: { + payload: input.payload as any, + parameter: input.parameter as any, + }, + }, + ) + + const mock: CommandFunctionContext = { + logger: logger.mock, + message, + emit: async (eventName: K, payload: Payload) => { + return eventList[eventName](eventName, payload) + }, + wrapInSpan: stubs.wrapInSpan.callsFake((_name, _opts, fn) => fn(getMockSpan())), + startActiveSpan: stubs.startActiveSpan.callsFake((_name, _opts, _context, fn) => fn(getMockSpan())), + service: getInvokeProxy(), + secrets: { + getSecret: stubs.getSecret.rejects(new Error('getSecret is not stubbed')), + setSecret: stubs.setSecret.rejects(new Error('setSecret is not stubbed')), + removeSecret: stubs.removeSecret.rejects(new Error('removeSecret is not stubbed')), + }, + configs: { + getConfig: stubs.getConfig.rejects(new Error('getConfig is not stubbed')), + setConfig: stubs.setConfig.rejects(new Error('setConfig is not stubbed')), + removeConfig: stubs.removeConfig.rejects(new Error('removeConfig is not stubbed')), + }, + states: { + getState: stubs.getState.rejects(new Error('getState is not stubbed')), + setState: stubs.setState.rejects(new Error('setState is not stubbed')), + removeState: stubs.removeState.rejects(new Error('removeState is not stubbed')), + }, + resources: resourcesProxy, + } + + return { + mock, + stubs, + } } diff --git a/packages/core/src/mocks/getCommandTransformContext.mock.ts b/packages/core/src/mocks/getCommandTransformContext.mock.ts index 4c608aa01..d0a981716 100644 --- a/packages/core/src/mocks/getCommandTransformContext.mock.ts +++ b/packages/core/src/mocks/getCommandTransformContext.mock.ts @@ -1,67 +1,98 @@ import type { SinonSandbox } from 'sinon' import { stub } from 'sinon' -import type { CommandTransformFunctionContext } from '../core/index.js' +import type { CommandTransformFunctionContext, EmptyObject } from '../core/index.js' import { getLoggerMock } from './getLogger.mock.js' import { getCommandMessageMock } from './messages/index.js' +const noop = () => { + // noop +} + /** * A function that returns a mock object for command transform function context * * @group Unit test helper * */ -export const getCommandTransformContextMock = ( - payload: MessagePayloadType, - parameter: MessageParamsType, - sandbox?: SinonSandbox, -) => { - const logger = getLoggerMock(sandbox) - const stubs = { - logger: logger.stubs, - wrapInSpan: sandbox?.stub() ?? stub(), - startActiveSpan: sandbox?.stub() ?? stub(), - getSecret: sandbox?.stub() ?? stub(), - setSecret: sandbox?.stub() ?? stub(), - removeSecret: sandbox?.stub() ?? stub(), - getConfig: sandbox?.stub() ?? stub(), - setConfig: sandbox?.stub() ?? stub(), - removeConfig: sandbox?.stub() ?? stub(), - getState: sandbox?.stub() ?? stub(), - setState: sandbox?.stub() ?? stub(), - removeState: sandbox?.stub() ?? stub(), - } +export const getCommandTransformContextMock = < + MessagePayloadType = unknown, + MessageParamsType = unknown, + Resources extends Record = EmptyObject, +>(input: { + payload: MessagePayloadType + parameter: MessageParamsType + resources?: Partial + sandbox?: SinonSandbox +}) => { + const logger = getLoggerMock(input.sandbox) + const providedResources: Partial = input.resources ?? ({} as Partial) + + const stubs = { + logger: logger.stubs, + wrapInSpan: input.sandbox?.stub() ?? stub(), + startActiveSpan: input.sandbox?.stub() ?? stub(), + getSecret: input.sandbox?.stub() ?? stub(), + setSecret: input.sandbox?.stub() ?? stub(), + removeSecret: input.sandbox?.stub() ?? stub(), + getConfig: input.sandbox?.stub() ?? stub(), + setConfig: input.sandbox?.stub() ?? stub(), + removeConfig: input.sandbox?.stub() ?? stub(), + getState: input.sandbox?.stub() ?? stub(), + setState: input.sandbox?.stub() ?? stub(), + removeState: input.sandbox?.stub() ?? stub(), + resource: {} as Partial, + } + + const resourcesProxy = new Proxy( + {}, + { + get(obj: Record, name) { + if (typeof name !== 'string' || name === 'then' || name === 'catch' || name === 'finally') { + throw new Error('Invalid property access on resources proxy') + } + if (Object.hasOwn(providedResources, name)) { + return providedResources[name] + } + if (!Object.hasOwn(stubs.resource, name)) { + throw new Error(`Resource ${name} not set or stubbed`) + } + return stubs.resource[name] + }, + }, + ) as Resources - const message = getCommandMessageMock({ - payload: { - payload, - parameter, - }, - }) + const message = getCommandMessageMock({ + payload: { + payload: input.payload, + parameter: input.parameter, + }, + }) - const mock: CommandTransformFunctionContext = { - logger: logger.mock, - message, - wrapInSpan: stubs.wrapInSpan.callsFake((_name, _opts, fn) => fn()), - startActiveSpan: stubs.startActiveSpan.callsFake((_name, _opts, _context, fn) => fn()), - secrets: { - getSecret: stubs.getSecret.rejects(new Error('getSecret is not stubbed')), - setSecret: stubs.setSecret.rejects(new Error('setSecret is not stubbed')), - removeSecret: stubs.removeSecret.rejects(new Error('removeSecret is not stubbed')), - }, - configs: { - getConfig: stubs.getConfig.rejects(new Error('getConfig is not stubbed')), - setConfig: stubs.setConfig.rejects(new Error('setConfig is not stubbed')), - removeConfig: stubs.removeConfig.rejects(new Error('removeConfig is not stubbed')), - }, - states: { - getState: stubs.getState.rejects(new Error('getState is not stubbed')), - setState: stubs.setState.rejects(new Error('setState is not stubbed')), - removeState: stubs.removeState.rejects(new Error('removeState is not stubbed')), - }, - } + const mock: CommandTransformFunctionContext = { + logger: logger.mock, + message, + wrapInSpan: stubs.wrapInSpan.callsFake((_name, _opts, fn) => fn()), + startActiveSpan: stubs.startActiveSpan.callsFake((_name, _opts, _context, fn) => fn()), + secrets: { + getSecret: stubs.getSecret.rejects(new Error('getSecret is not stubbed')), + setSecret: stubs.setSecret.rejects(new Error('setSecret is not stubbed')), + removeSecret: stubs.removeSecret.rejects(new Error('removeSecret is not stubbed')), + }, + configs: { + getConfig: stubs.getConfig.rejects(new Error('getConfig is not stubbed')), + setConfig: stubs.setConfig.rejects(new Error('setConfig is not stubbed')), + removeConfig: stubs.removeConfig.rejects(new Error('removeConfig is not stubbed')), + }, + states: { + getState: stubs.getState.rejects(new Error('getState is not stubbed')), + setState: stubs.setState.rejects(new Error('setState is not stubbed')), + removeState: stubs.removeState.rejects(new Error('removeState is not stubbed')), + }, + resources: resourcesProxy, + } - return { - mock, - stubs, - } + return { + mock, + stubs, + } } diff --git a/packages/core/src/mocks/getEventBridge.mock.ts b/packages/core/src/mocks/getEventBridge.mock.ts index de63cf116..a4273df25 100644 --- a/packages/core/src/mocks/getEventBridge.mock.ts +++ b/packages/core/src/mocks/getEventBridge.mock.ts @@ -9,46 +9,46 @@ import type { EventBridge } from '../core/index.js' * @group Unit test helper */ export const getEventBridgeMock = (sandbox?: SinonSandbox): { mock: EventBridge; stubs: Record } => { - const emitMessage = sandbox?.stub() ?? stub() - const registerCommand = sandbox?.stub() ?? stub() - const registerSubscription = sandbox?.stub() ?? stub() - const unregisterCommand = sandbox?.stub() ?? stub() - const unregisterSubscription = sandbox?.stub() ?? stub() - const invoke = sandbox?.stub() ?? stub() - const start = sandbox?.stub() ?? stub() - const isReady = sandbox?.stub().resolves(true) ?? stub().resolves(true) - const isHealthy = sandbox?.stub().resolves(true) ?? stub().resolves(true) - const destroy = sandbox?.stub().resolves() ?? stub().resolves() + const emitMessage = sandbox?.stub() ?? stub() + const registerCommand = sandbox?.stub() ?? stub() + const registerSubscription = sandbox?.stub() ?? stub() + const unregisterCommand = sandbox?.stub() ?? stub() + const unregisterSubscription = sandbox?.stub() ?? stub() + const invoke = sandbox?.stub() ?? stub() + const start = sandbox?.stub() ?? stub() + const isReady = sandbox?.stub().resolves(true) ?? stub().resolves(true) + const isHealthy = sandbox?.stub().resolves(true) ?? stub().resolves(true) + const destroy = sandbox?.stub().resolves() ?? stub().resolves() - const mock: EventBridge = { - name: 'EventBridgeMock', - instanceId: 'mockedInstanceId', - defaultCommandTimeout: 30000, - emitMessage, - registerCommand, - registerSubscription, - unregisterCommand, - unregisterSubscription, - invoke, - start, - isReady, - isHealthy, - destroy, - } + const mock: EventBridge = { + name: 'EventBridgeMock', + instanceId: 'mockedInstanceId', + defaultCommandTimeout: 30000, + emitMessage, + registerCommand, + registerSubscription, + unregisterCommand, + unregisterSubscription, + invoke, + start, + isReady, + isHealthy, + destroy, + } - return { - stubs: { - emitMessage, - registerCommand, - registerSubscription, - unregisterCommand, - unregisterSubscription, - invoke, - start, - isReady, - isHealthy, - destroy, - }, - mock, - } + return { + stubs: { + emitMessage, + registerCommand, + registerSubscription, + unregisterCommand, + unregisterSubscription, + invoke, + start, + isReady, + isHealthy, + destroy, + }, + mock, + } } diff --git a/packages/core/src/mocks/getLogger.mock.ts b/packages/core/src/mocks/getLogger.mock.ts index 688b01f36..9e385d2d2 100644 --- a/packages/core/src/mocks/getLogger.mock.ts +++ b/packages/core/src/mocks/getLogger.mock.ts @@ -9,40 +9,40 @@ import type { Logger } from '../core/index.js' * @group Unit test helper */ export const getLoggerMock = (sandbox?: SinonSandbox) => { - const info = sandbox?.stub() ?? stub() - const error = sandbox?.stub() ?? stub() - const warn = sandbox?.stub() ?? stub() - const debug = sandbox?.stub() ?? stub() - const trace = sandbox?.stub() ?? stub() - const fatal = sandbox?.stub() ?? stub() + const info = sandbox?.stub() ?? stub() + const error = sandbox?.stub() ?? stub() + const warn = sandbox?.stub() ?? stub() + const debug = sandbox?.stub() ?? stub() + const trace = sandbox?.stub() ?? stub() + const fatal = sandbox?.stub() ?? stub() - const mock: Logger = { - info, - error, - warn, - debug, - trace, - fatal, - getChildLogger: () => mock, - } + const mock: Logger = { + info, + error, + warn, + debug, + trace, + fatal, + getChildLogger: () => mock, + } - return { - stubs: { - info, - error, - warn, - debug, - trace, - fatal, - }, - mock, - } + return { + stubs: { + info, + error, + warn, + debug, + trace, + fatal, + }, + mock, + } } export type LoggerStubs = { - info: SinonStub - error: SinonStub - warn: SinonStub - debug: SinonStub - trace: SinonStub + info: SinonStub + error: SinonStub + warn: SinonStub + debug: SinonStub + trace: SinonStub } diff --git a/packages/core/src/mocks/getSubscriptionContext.mock.ts b/packages/core/src/mocks/getSubscriptionContext.mock.ts index 47535b58c..f0279f9cb 100644 --- a/packages/core/src/mocks/getSubscriptionContext.mock.ts +++ b/packages/core/src/mocks/getSubscriptionContext.mock.ts @@ -3,11 +3,12 @@ import type { SinonSandbox, SinonStub } from 'sinon' import { stub } from 'sinon' import type { - EBMessage, - EBMessageAddress, - FromEmitToOtherType, - FromInvokeToOtherType, - SubscriptionFunctionContext, + EBMessage, + EBMessageAddress, + FromEmitToOtherType, + FromInvokeToOtherType, + InvokeList, + SubscriptionFunctionContext, } from '../core/index.js' import { getLoggerMock } from './getLogger.mock.js' @@ -16,153 +17,174 @@ import { getLoggerMock } from './getLogger.mock.js' * * @group Unit test helper * */ -export const getSubscriptionContextMock = ( - message: EBMessage, - sandbox?: SinonSandbox, - _invokes?: FromInvokeToOtherType< - Invokes, - { outputSchema?: Schema; payloadSchema?: Schema; parameterSchema?: Schema } - >, - emitList?: EmitListType, -) => { - const logger = getLoggerMock(sandbox) +export const getSubscriptionContextMock = < + Resources extends Record, + Invokes extends InvokeList, + EmitList extends Record, +>(input: { + message: EBMessage + sandbox?: SinonSandbox + invokes: FromInvokeToOtherType + emitList: FromEmitToOtherType + resources?: Partial +}) => { + const logger = getLoggerMock(input.sandbox) - const getMockSpan = () => { - return { - spanContext: () => { - return { - traceId: 'fake', - spanId: 'fake', - isRemote: false, - traceFlags: 0, - } - }, - setAttribute: sandbox?.stub() ?? stub(), - setAttributes: sandbox?.stub() ?? stub(), - addEvent: sandbox?.stub() ?? stub(), - setStatus: sandbox?.stub() ?? stub(), - updateName: sandbox?.stub() ?? stub(), - end: sandbox?.stub() ?? stub(), - isRecording: () => true, - recordException: (sandbox?.stub() ?? stub()).callsFake((err: any) => { - // eslint-disable-next-line no-console - console.error(err) - }), - } - } + const getMockSpan = () => { + return { + spanContext: () => { + return { + traceId: 'fake', + spanId: 'fake', + isRemote: false, + traceFlags: 0, + } + }, + setAttribute: input.sandbox?.stub() ?? stub(), + setAttributes: input.sandbox?.stub() ?? stub(), + addEvent: input.sandbox?.stub() ?? stub(), + setStatus: input.sandbox?.stub() ?? stub(), + updateName: input.sandbox?.stub() ?? stub(), + end: input.sandbox?.stub() ?? stub(), + isRecording: () => true, + recordException: (input.sandbox?.stub() ?? stub()).callsFake((err: unknown) => { + // biome-ignore lint/suspicious/noConsole: no logger available + console.error(err) + }), + } + } - const invokeMocks: Record>> = {} + const invokeMocks: Record>> = {} - const getInvokeProxy = (address?: EBMessageAddress, lvl = 0): TFaux => { - const adr = { - serviceName: '', - serviceTarget: '', - serviceVersion: '', - ...address, - } + const getInvokeProxy = (address?: EBMessageAddress, lvl = 0): TFaux => { + const adr = { + serviceName: '', + serviceTarget: '', + serviceVersion: '', + ...address, + } - return new Proxy(() => {}, { - get(obj: Record, name) { - if (typeof name !== 'string' || name === 'then' || name === 'catch' || name === 'finally') { - return undefined - } + return new Proxy(() => {}, { + get(obj: Record, name) { + if (typeof name !== 'string' || name === 'then' || name === 'catch' || name === 'finally') { + return undefined + } - const x = obj[name] - if (lvl === 0) { - const na = { - ...adr, - serviceName: name, - } - if (!invokeMocks[na.serviceName]) { - invokeMocks[na.serviceName] = {} - } - return getInvokeProxy(na, lvl + 1) - } - if (lvl === 1) { - const na = { - ...adr, - serviceVersion: name, - } - if (!invokeMocks[na.serviceName][na.serviceVersion]) { - invokeMocks[na.serviceName][na.serviceVersion] = {} - } - return getInvokeProxy(na, lvl + 1) - } + const x = obj[name] + if (lvl === 0) { + const na = { + ...adr, + serviceName: name, + } + if (!invokeMocks[na.serviceName]) { + invokeMocks[na.serviceName] = {} + } + return getInvokeProxy(na, lvl + 1) + } + if (lvl === 1) { + const na = { + ...adr, + serviceVersion: name, + } + if (!invokeMocks[na.serviceName][na.serviceVersion]) { + invokeMocks[na.serviceName][na.serviceVersion] = {} + } + return getInvokeProxy(na, lvl + 1) + } - if (lvl === 2) { - const na = { - ...adr, - serviceTarget: name, - } - if (!invokeMocks[na.serviceName][na.serviceVersion][na.serviceTarget]) { - invokeMocks[na.serviceName][na.serviceVersion][na.serviceTarget] = sandbox?.stub() ?? stub() + if (lvl === 2) { + const na = { + ...adr, + serviceTarget: name, + } + if (!invokeMocks[na.serviceName][na.serviceVersion][na.serviceTarget]) { + invokeMocks[na.serviceName][na.serviceVersion][na.serviceTarget] = input.sandbox?.stub() ?? stub() - invokeMocks[na.serviceName][na.serviceVersion][na.serviceTarget].rejects( - new Error( - `invocation of ${na.serviceTarget} in service ${na.serviceName} version ${na.serviceVersion} is not stubbed`, - ), - ) - } - return invokeMocks[na.serviceName]?.[na.serviceVersion]?.[na.serviceTarget] - } - }, - }) as TFaux - } + invokeMocks[na.serviceName][na.serviceVersion][na.serviceTarget].rejects( + new Error( + `invocation of ${na.serviceTarget} in service ${na.serviceName} version ${na.serviceVersion} is not stubbed`, + ), + ) + } + return invokeMocks[na.serviceName]?.[na.serviceVersion]?.[na.serviceTarget] + } + }, + }) as TFaux + } - const eventList = Object.keys(emitList ?? {}).reduce((prev, current) => { - return { - ...prev, - [current]: sandbox?.stub() ?? stub().resolves(), - } - }, {}) as FromEmitToOtherType + const resourceMocks: Record = {} - const stubs = { - logger: logger.stubs, - emit: eventList, - invoke: sandbox?.stub() ?? stub(), - wrapInSpan: sandbox?.stub() ?? stub(), - startActiveSpan: sandbox?.stub() ?? stub(), - getSecret: sandbox?.stub() ?? stub(), - setSecret: sandbox?.stub() ?? stub(), - removeSecret: sandbox?.stub() ?? stub(), - getConfig: sandbox?.stub() ?? stub(), - setConfig: sandbox?.stub() ?? stub(), - removeConfig: sandbox?.stub() ?? stub(), - getState: sandbox?.stub() ?? stub(), - setState: sandbox?.stub() ?? stub(), - removeState: sandbox?.stub() ?? stub(), - service: getInvokeProxy>(), - } + const getResourceProxy = (): TFaux => { + return new Proxy(() => {}, { + get(obj: Record, name) { + if (typeof name !== 'string' || name === 'then' || name === 'catch' || name === 'finally') { + return undefined + } + if (!resourceMocks[name]) { + resourceMocks[name] = input.sandbox?.stub() ?? stub() + resourceMocks[name].throws(`Resource ${name} not mocked`) + } + return resourceMocks[name] + }, + }) as TFaux + } - const mock: SubscriptionFunctionContext = { - logger: logger.mock, - message, - emit: async (eventName: K, payload: Payload) => { - return eventList[eventName](eventName, payload) - }, - invoke: stubs.invoke.rejects(new Error('Invoke is not stubbed')), - wrapInSpan: stubs.wrapInSpan.callsFake((_name, _opts, fn) => fn(getMockSpan())), - startActiveSpan: stubs.startActiveSpan.callsFake((_name, _opts, _context, fn) => fn(getMockSpan())), - service: getInvokeProxy(), - secrets: { - getSecret: stubs.getSecret.rejects(new Error('getSecret is not stubbed')), - setSecret: stubs.setSecret.rejects(new Error('setSecret is not stubbed')), - removeSecret: stubs.removeSecret.rejects(new Error('removeSecret is not stubbed')), - }, - configs: { - getConfig: stubs.getConfig.rejects(new Error('getConfig is not stubbed')), - setConfig: stubs.setConfig.rejects(new Error('setConfig is not stubbed')), - removeConfig: stubs.removeConfig.rejects(new Error('removeConfig is not stubbed')), - }, - states: { - getState: stubs.getState.rejects(new Error('getState is not stubbed')), - setState: stubs.setState.rejects(new Error('setState is not stubbed')), - removeState: stubs.removeState.rejects(new Error('removeState is not stubbed')), - }, - } + const eventList = Object.keys(input.emitList ?? {}).reduce((prev, current) => { + return { + // biome-ignore lint/performance/noAccumulatingSpread: + ...prev, + [current]: input.sandbox?.stub() ?? stub().resolves(), + } + }, {}) as FromEmitToOtherType - return { - mock, - stubs, - } + const stubs = { + logger: logger.stubs, + emit: eventList, + invoke: input.sandbox?.stub() ?? stub(), + wrapInSpan: input.sandbox?.stub() ?? stub(), + startActiveSpan: input.sandbox?.stub() ?? stub(), + getSecret: input.sandbox?.stub() ?? stub(), + setSecret: input.sandbox?.stub() ?? stub(), + removeSecret: input.sandbox?.stub() ?? stub(), + getConfig: input.sandbox?.stub() ?? stub(), + setConfig: input.sandbox?.stub() ?? stub(), + removeConfig: input.sandbox?.stub() ?? stub(), + getState: input.sandbox?.stub() ?? stub(), + setState: input.sandbox?.stub() ?? stub(), + removeState: input.sandbox?.stub() ?? stub(), + service: getInvokeProxy>(), + resources: getResourceProxy(), + } + + const mock: SubscriptionFunctionContext = { + logger: logger.mock, + message: input.message, + emit: async (eventName: K, payload: Payload) => { + return eventList[eventName](eventName, payload) + }, + wrapInSpan: stubs.wrapInSpan.callsFake((_name, _opts, fn) => fn(getMockSpan())), + startActiveSpan: stubs.startActiveSpan.callsFake((_name, _opts, _context, fn) => fn(getMockSpan())), + service: getInvokeProxy(), + secrets: { + getSecret: stubs.getSecret.rejects(new Error('getSecret is not stubbed')), + setSecret: stubs.setSecret.rejects(new Error('setSecret is not stubbed')), + removeSecret: stubs.removeSecret.rejects(new Error('removeSecret is not stubbed')), + }, + configs: { + getConfig: stubs.getConfig.rejects(new Error('getConfig is not stubbed')), + setConfig: stubs.setConfig.rejects(new Error('setConfig is not stubbed')), + removeConfig: stubs.removeConfig.rejects(new Error('removeConfig is not stubbed')), + }, + states: { + getState: stubs.getState.rejects(new Error('getState is not stubbed')), + setState: stubs.setState.rejects(new Error('setState is not stubbed')), + removeState: stubs.removeState.rejects(new Error('removeState is not stubbed')), + }, + resources: getResourceProxy(), + } + + return { + mock, + stubs, + } } diff --git a/packages/core/src/mocks/getSubscriptionTransformContext.mock.ts b/packages/core/src/mocks/getSubscriptionTransformContext.mock.ts index 1abff6847..0f871a1e1 100644 --- a/packages/core/src/mocks/getSubscriptionTransformContext.mock.ts +++ b/packages/core/src/mocks/getSubscriptionTransformContext.mock.ts @@ -1,7 +1,7 @@ import type { SinonSandbox } from 'sinon' import { stub } from 'sinon' -import type { EBMessage, SubscriptionTransformFunctionContext } from '../core/index.js' +import type { EBMessage, EmptyObject, SubscriptionTransformFunctionContext } from '../core/index.js' import { getLoggerMock } from './getLogger.mock.js' /** @@ -9,47 +9,73 @@ import { getLoggerMock } from './getLogger.mock.js' * * @group Unit test helper * */ -export const getSubscriptionTransformContextMock = (message: EBMessage, sandbox?: SinonSandbox) => { - const logger = getLoggerMock(sandbox) - const stubs = { - logger: logger.stubs, - wrapInSpan: sandbox?.stub() ?? stub(), - startActiveSpan: sandbox?.stub() ?? stub(), - getSecret: sandbox?.stub() ?? stub(), - setSecret: sandbox?.stub() ?? stub(), - removeSecret: sandbox?.stub() ?? stub(), - getConfig: sandbox?.stub() ?? stub(), - setConfig: sandbox?.stub() ?? stub(), - removeConfig: sandbox?.stub() ?? stub(), - getState: sandbox?.stub() ?? stub(), - setState: sandbox?.stub() ?? stub(), - removeState: sandbox?.stub() ?? stub(), - } +export const getSubscriptionTransformContextMock = = EmptyObject>(input: { + message: EBMessage + resources?: Partial + sandbox?: SinonSandbox +}) => { + const logger = getLoggerMock(input.sandbox) + const providedResources: Partial = input.resources ?? ({} as Partial) - const mock: SubscriptionTransformFunctionContext = { - logger: logger.mock, - message, - wrapInSpan: stubs.wrapInSpan.callsFake((_name, _opts, fn) => fn()), - startActiveSpan: stubs.startActiveSpan.callsFake((_name, _opts, _context, fn) => fn()), - secrets: { - getSecret: stubs.getSecret.rejects(new Error('getSecret is not stubbed')), - setSecret: stubs.setSecret.rejects(new Error('setSecret is not stubbed')), - removeSecret: stubs.removeSecret.rejects(new Error('removeSecret is not stubbed')), - }, - configs: { - getConfig: stubs.getConfig.rejects(new Error('getConfig is not stubbed')), - setConfig: stubs.setConfig.rejects(new Error('setConfig is not stubbed')), - removeConfig: stubs.removeConfig.rejects(new Error('removeConfig is not stubbed')), - }, - states: { - getState: stubs.getState.rejects(new Error('getState is not stubbed')), - setState: stubs.setState.rejects(new Error('setState is not stubbed')), - removeState: stubs.removeState.rejects(new Error('removeState is not stubbed')), - }, - } + const stubs = { + logger: logger.stubs, + wrapInSpan: input.sandbox?.stub() ?? stub(), + startActiveSpan: input.sandbox?.stub() ?? stub(), + getSecret: input.sandbox?.stub() ?? stub(), + setSecret: input.sandbox?.stub() ?? stub(), + removeSecret: input.sandbox?.stub() ?? stub(), + getConfig: input.sandbox?.stub() ?? stub(), + setConfig: input.sandbox?.stub() ?? stub(), + removeConfig: input.sandbox?.stub() ?? stub(), + getState: input.sandbox?.stub() ?? stub(), + setState: input.sandbox?.stub() ?? stub(), + removeState: input.sandbox?.stub() ?? stub(), + resources: {} as Partial, + } - return { - mock, - stubs, - } + const resourcesProxy = new Proxy( + {}, + { + get(obj: Record, name) { + if (typeof name !== 'string' || name === 'then' || name === 'catch' || name === 'finally') { + throw new Error('Invalid property access on resources proxy') + } + if (Object.hasOwn(providedResources, name)) { + return providedResources[name] + } + if (!Object.hasOwn(stubs.resources, name)) { + throw new Error(`Resource ${name} not set or stubbed`) + } + return stubs.resources[name] + }, + }, + ) as Resources + + const mock: SubscriptionTransformFunctionContext = { + logger: logger.mock, + message: input.message, + wrapInSpan: stubs.wrapInSpan.callsFake((_name, _opts, fn) => fn()), + startActiveSpan: stubs.startActiveSpan.callsFake((_name, _opts, _context, fn) => fn()), + secrets: { + getSecret: stubs.getSecret.rejects(new Error('getSecret is not stubbed')), + setSecret: stubs.setSecret.rejects(new Error('setSecret is not stubbed')), + removeSecret: stubs.removeSecret.rejects(new Error('removeSecret is not stubbed')), + }, + configs: { + getConfig: stubs.getConfig.rejects(new Error('getConfig is not stubbed')), + setConfig: stubs.setConfig.rejects(new Error('setConfig is not stubbed')), + removeConfig: stubs.removeConfig.rejects(new Error('removeConfig is not stubbed')), + }, + states: { + getState: stubs.getState.rejects(new Error('getState is not stubbed')), + setState: stubs.setState.rejects(new Error('setState is not stubbed')), + removeState: stubs.removeState.rejects(new Error('removeState is not stubbed')), + }, + resources: resourcesProxy, + } + + return { + mock, + stubs, + } } diff --git a/packages/core/src/mocks/messages/getCommandErrorMessage.mock.ts b/packages/core/src/mocks/messages/getCommandErrorMessage.mock.ts index bf9abdeba..08672f970 100644 --- a/packages/core/src/mocks/messages/getCommandErrorMessage.mock.ts +++ b/packages/core/src/mocks/messages/getCommandErrorMessage.mock.ts @@ -1,10 +1,10 @@ import { - type Command, - type CommandErrorResponse, - createErrorResponse, - getNewInstanceId, - type HandledError, - type UnhandledError, + type Command, + type CommandErrorResponse, + type HandledError, + type UnhandledError, + createErrorResponse, + getNewInstanceId, } from '../../core/index.js' import { getCommandMessageMock } from './getCommandMessage.mock.js' @@ -14,15 +14,15 @@ import { getCommandMessageMock } from './getCommandMessage.mock.js' * @group Unit test helper */ export const getCommandErrorMessageMock = ( - error?: HandledError | UnhandledError, - input?: Partial, - commandMessage?: Command, + error?: HandledError | UnhandledError, + input?: Partial, + commandMessage?: Command, ): Readonly => { - const cmdMessage: Readonly> = commandMessage ?? getCommandMessageMock() + const cmdMessage: Readonly> = commandMessage ?? getCommandMessageMock() - const successResponse: Readonly = Object.freeze({ - ...createErrorResponse(getNewInstanceId(), cmdMessage, error?.errorCode, error), - ...input, - }) - return successResponse + const successResponse: Readonly = Object.freeze({ + ...createErrorResponse(getNewInstanceId(), cmdMessage, error?.errorCode, error), + ...input, + }) + return successResponse } diff --git a/packages/core/src/mocks/messages/getCommandMessage.mock.ts b/packages/core/src/mocks/messages/getCommandMessage.mock.ts index 2a0bbb92f..455b593c6 100644 --- a/packages/core/src/mocks/messages/getCommandMessage.mock.ts +++ b/packages/core/src/mocks/messages/getCommandMessage.mock.ts @@ -1,10 +1,10 @@ import type { Command } from '../../core/index.js' import { - EBMessageType, - getNewCorrelationId, - getNewEBMessageId, - getNewInstanceId, - getNewTraceId, + EBMessageType, + getNewCorrelationId, + getNewEBMessageId, + getNewInstanceId, + getNewTraceId, } from '../../core/index.js' /** @@ -12,41 +12,41 @@ import { * * @group Unit test helper * */ -export const getCommandMessageMock = ( - input?: Partial> & { - payload?: { - payload?: Payload - parameter?: Parameter - } - }, +export const getCommandMessageMock = ( + input?: Partial> & { + payload?: { + payload?: Payload + parameter?: Parameter + } + }, ): Readonly> => { - const commandMessage: Readonly> = Object.freeze({ - id: getNewEBMessageId(), - timestamp: Date.now(), - messageType: EBMessageType.Command, - correlationId: getNewCorrelationId(), - traceId: getNewTraceId(), - principalId: 'mocked-principal-id', - tenantId: 'mocked-tenant-id', - contentType: 'application/json', - contentEncoding: 'utf-8', - sender: { - serviceName: 'mocked_sender', - serviceVersion: '1', - serviceTarget: 'mockedSenderFunction', - instanceId: getNewInstanceId(), - }, - receiver: { - serviceName: 'mocked_receiver', - serviceVersion: '1', - serviceTarget: 'mockedReceiverFunction', - instanceId: getNewInstanceId(), - }, - payload: { - payload: input?.payload?.payload as Payload, - parameter: (input?.payload?.parameter ?? {}) as Parameter, - }, - ...input, - }) - return commandMessage + const commandMessage: Readonly> = Object.freeze({ + id: getNewEBMessageId(), + timestamp: Date.now(), + messageType: EBMessageType.Command, + correlationId: getNewCorrelationId(), + traceId: getNewTraceId(), + principalId: 'mocked-principal-id', + tenantId: 'mocked-tenant-id', + contentType: 'application/json', + contentEncoding: 'utf-8', + sender: { + serviceName: 'mocked_sender', + serviceVersion: '1', + serviceTarget: 'mockedSenderFunction', + instanceId: getNewInstanceId(), + }, + receiver: { + serviceName: 'mocked_receiver', + serviceVersion: '1', + serviceTarget: 'mockedReceiverFunction', + instanceId: getNewInstanceId(), + }, + payload: { + payload: input?.payload?.payload as Payload, + parameter: (input?.payload?.parameter ?? {}) as Parameter, + }, + ...input, + }) + return commandMessage } diff --git a/packages/core/src/mocks/messages/getCommandSuccessMessage.mock.ts b/packages/core/src/mocks/messages/getCommandSuccessMessage.mock.ts index 0758627d6..a5c565368 100644 --- a/packages/core/src/mocks/messages/getCommandSuccessMessage.mock.ts +++ b/packages/core/src/mocks/messages/getCommandSuccessMessage.mock.ts @@ -8,16 +8,16 @@ import { getCommandMessageMock } from './getCommandMessage.mock.js' * @group Unit test helper * */ export const getCommandSuccessMessageMock = ( - payload: PayloadType, - input?: Partial>, - commandMessage?: Command, + payload: PayloadType, + input?: Partial>, + commandMessage?: Command, ): Readonly> => { - const cmdMessage: Readonly> = commandMessage ?? getCommandMessageMock() + const cmdMessage: Readonly> = commandMessage ?? getCommandMessageMock() - const successResponse: Readonly> = Object.freeze({ - ...createSuccessResponse(commandMessage?.receiver.instanceId ?? getNewInstanceId(), cmdMessage, payload), - ...input, - }) + const successResponse: Readonly> = Object.freeze({ + ...createSuccessResponse(commandMessage?.receiver.instanceId ?? getNewInstanceId(), cmdMessage, payload), + ...input, + }) - return successResponse + return successResponse } diff --git a/packages/core/src/mocks/messages/getCustomMessage.mock.ts b/packages/core/src/mocks/messages/getCustomMessage.mock.ts index c2aecc036..4307d95d0 100644 --- a/packages/core/src/mocks/messages/getCustomMessage.mock.ts +++ b/packages/core/src/mocks/messages/getCustomMessage.mock.ts @@ -1,10 +1,10 @@ import type { CustomMessage } from '../../core/index.js' import { - EBMessageType, - getNewCorrelationId, - getNewEBMessageId, - getNewInstanceId, - getNewTraceId, + EBMessageType, + getNewCorrelationId, + getNewEBMessageId, + getNewInstanceId, + getNewTraceId, } from '../../core/index.js' /** @@ -13,29 +13,29 @@ import { * @group Unit test helper * */ export const getCustomMessageMessageMock = ( - eventName: string, - payload: PayloadType, - input?: Partial>, + eventName: string, + payload: PayloadType, + input?: Partial>, ): Readonly> => { - const customMessage: Readonly> = Object.freeze({ - id: getNewEBMessageId(), - timestamp: Date.now(), - contentType: 'application/json', - contentEncoding: 'utf-8', - messageType: EBMessageType.CustomMessage, - correlationId: getNewCorrelationId(), - traceId: getNewTraceId(), - principalId: 'mocked-principal-id', - tenantId: 'mocked-tenant-id', - eventName, - sender: { - serviceName: 'mocked_sender', - serviceVersion: '1', - serviceTarget: 'mockedSenderFunction', - instanceId: getNewInstanceId(), - }, - ...input, - payload, - }) - return customMessage + const customMessage: Readonly> = Object.freeze({ + id: getNewEBMessageId(), + timestamp: Date.now(), + contentType: 'application/json', + contentEncoding: 'utf-8', + messageType: EBMessageType.CustomMessage, + correlationId: getNewCorrelationId(), + traceId: getNewTraceId(), + principalId: 'mocked-principal-id', + tenantId: 'mocked-tenant-id', + eventName, + sender: { + serviceName: 'mocked_sender', + serviceVersion: '1', + serviceTarget: 'mockedSenderFunction', + instanceId: getNewInstanceId(), + }, + ...input, + payload, + }) + return customMessage } diff --git a/packages/core/src/zodOpenApi/validationToSchema.test.ts b/packages/core/src/zodOpenApi/validationToSchema.test.ts index 13e9c55d5..2b874ea5a 100644 --- a/packages/core/src/zodOpenApi/validationToSchema.test.ts +++ b/packages/core/src/zodOpenApi/validationToSchema.test.ts @@ -4,51 +4,51 @@ import { z } from 'zod' import { validationToSchema } from './validationToSchema.js' describe('validateToSchema', () => { - it('converts zod schema', async () => { - const schema = z.object({ - one: z.string(), - two: z.number().optional(), - }) + it('converts zod schema', async () => { + const schema = z.object({ + one: z.string(), + two: z.number().optional(), + }) - const result = await validationToSchema(schema) + const result = await validationToSchema(schema) - expect(result).toStrictEqual({ - properties: { - one: { - type: 'string', - }, - two: { - type: 'number', - }, - }, - required: ['one'], - type: 'object', - }) - }) + expect(result).toStrictEqual({ + properties: { + one: { + type: 'string', + }, + two: { + type: 'number', + }, + }, + required: ['one'], + type: 'object', + }) + }) - it('converts yup schema', async () => { - const schema = yup.object({ - one: yup.string().required(), - two: yup.number(), - }) + it('converts yup schema', async () => { + const schema = yup.object({ + one: yup.string().required(), + two: yup.number(), + }) - const result = await validationToSchema(schema) + const result = await validationToSchema(schema) - expect(result).toStrictEqual({ - properties: { - one: { - type: 'string', - }, - two: { - type: 'number', - }, - }, - required: ['one'], - type: 'object', - default: { - one: undefined, - two: undefined, - }, - }) - }) + expect(result).toStrictEqual({ + properties: { + one: { + type: 'string', + }, + two: { + type: 'number', + }, + }, + required: ['one'], + type: 'object', + default: { + one: undefined, + two: undefined, + }, + }) + }) }) diff --git a/packages/core/src/zodOpenApi/validationToSchema.ts b/packages/core/src/zodOpenApi/validationToSchema.ts index 0d24a1425..1b80da68f 100644 --- a/packages/core/src/zodOpenApi/validationToSchema.ts +++ b/packages/core/src/zodOpenApi/validationToSchema.ts @@ -7,19 +7,22 @@ import { ZodType } from 'zod' import { generateSchema } from './zodOpenApi.impl.js' export const validationToSchema = async (schema?: T): Promise => { - if (!schema) { - return - } - if (schema instanceof ZodType) { - return generateSchema(schema) - } - try { - const jsonSchema = await toJSONSchema(schema) - // nothing more needed as we use OpenAPI 3.1 which is valid JSON Schema - return jsonSchema as SchemaObject - } catch (error) { - console.error(error) - console.error('Did you installed peer dependencies?') - console.error('requires: @typeschema/[YOUR_SCHEMA_LIB]') - } + if (!schema) { + return + } + if (schema instanceof ZodType) { + return generateSchema(schema) + } + try { + const jsonSchema = await toJSONSchema(schema) + // nothing more needed as we use OpenAPI 3.1 which is valid JSON Schema + return jsonSchema as SchemaObject + } catch (error) { + // biome-ignore lint/suspicious/noConsole: Required + console.error(error) + // biome-ignore lint/suspicious/noConsole: Required + console.error('Did you installed peer dependencies?') + // biome-ignore lint/suspicious/noConsole: Required + console.error('requires: @typeschema/[YOUR_SCHEMA_LIB]') + } } diff --git a/packages/core/src/zodOpenApi/zodOpenApi.impl.ts b/packages/core/src/zodOpenApi/zodOpenApi.impl.ts index 2367ecce0..9780fd1fa 100644 --- a/packages/core/src/zodOpenApi/zodOpenApi.impl.ts +++ b/packages/core/src/zodOpenApi/zodOpenApi.impl.ts @@ -10,429 +10,430 @@ import { z } from 'zod' * @link https://github.com/anatine/zod-plugins/tree/main/packages/zod-openapi */ export interface OpenApiZodAny extends ZodTypeAny { - metaOpenApi?: SchemaObject | SchemaObject[] + metaOpenApi?: SchemaObject | SchemaObject[] } interface OpenApiZodAnyObject extends AnyZodObject { - metaOpenApi?: SchemaObject | SchemaObject[] + metaOpenApi?: SchemaObject | SchemaObject[] } interface ParsingArgs { - zodRef: T - schemas: SchemaObject[] - useOutput?: boolean + zodRef: T + schemas: SchemaObject[] + useOutput?: boolean } export function extendApi(schema: T, SchemaObject: SchemaObject = {}): T { - schema.metaOpenApi = Object.assign(schema.metaOpenApi ?? {}, SchemaObject) - return schema + schema.metaOpenApi = Object.assign(schema.metaOpenApi ?? {}, SchemaObject) + return schema } function iterateZodObject({ zodRef, useOutput }: ParsingArgs) { - return Object.keys(zodRef.shape).reduce( - (carry, key) => ({ - ...carry, - [key]: generateSchema(zodRef.shape[key], useOutput), - }), - {} as Record, - ) + return Object.keys(zodRef.shape).reduce( + (carry, key) => ({ + // biome-ignore lint/performance/noAccumulatingSpread: + ...carry, + [key]: generateSchema(zodRef.shape[key], useOutput), + }), + {} as Record, + ) } function parseTransformation({ - zodRef, - schemas, - useOutput, + zodRef, + schemas, + useOutput, }: ParsingArgs | z.ZodEffects>): SchemaObject { - const input = generateSchema(zodRef._def.schema, useOutput) - - let output = 'undefined' - if (useOutput && zodRef._def.effect) { - const effect = zodRef._def.effect.type === 'transform' ? zodRef._def.effect : null - if (effect && 'transform' in effect) { - try { - output = typeof effect.transform( - ['integer', 'number'].includes(`${input.type}`) - ? 0 - : input.type === 'string' - ? '' - : input.type === 'boolean' - ? false - : input.type === 'object' - ? {} - : input.type === 'null' - ? null - : input.type === 'array' - ? [] - : undefined, - { addIssue: () => undefined, path: [] }, // TODO: Discover if context is necessary here - ) - } catch (e) { - /**/ - } - } - } - return merge( - { - ...(zodRef.description ? { description: zodRef.description } : {}), - ...input, - ...(['number', 'string', 'boolean', 'null'].includes(output) - ? { - type: output as 'number' | 'string' | 'boolean' | 'null', - } - : {}), - }, - ...schemas, - ) + const input = generateSchema(zodRef._def.schema, useOutput) + + let output = 'undefined' + if (useOutput && zodRef._def.effect) { + const effect = zodRef._def.effect.type === 'transform' ? zodRef._def.effect : null + if (effect && 'transform' in effect) { + try { + output = typeof effect.transform( + ['integer', 'number'].includes(`${input.type}`) + ? 0 + : input.type === 'string' + ? '' + : input.type === 'boolean' + ? false + : input.type === 'object' + ? {} + : input.type === 'null' + ? null + : input.type === 'array' + ? [] + : undefined, + { addIssue: () => undefined, path: [] }, // TODO: Discover if context is necessary here + ) + } catch (e) { + /**/ + } + } + } + return merge( + { + ...(zodRef.description ? { description: zodRef.description } : {}), + ...input, + ...(['number', 'string', 'boolean', 'null'].includes(output) + ? { + type: output as 'number' | 'string' | 'boolean' | 'null', + } + : {}), + }, + ...schemas, + ) } function parseString({ zodRef, schemas }: ParsingArgs): SchemaObject { - const baseSchema: SchemaObject = { - type: 'string', - } - const { checks = [] } = zodRef._def - checks.forEach((item) => { - switch (item.kind) { - case 'email': - baseSchema.format = 'email' - break - case 'uuid': - baseSchema.format = 'uuid' - break - case 'cuid': - baseSchema.format = 'cuid' - break - case 'url': - baseSchema.format = 'uri' - break - case 'datetime': - baseSchema.format = 'date-time' - break - case 'length': - baseSchema.minLength = item.value - baseSchema.maxLength = item.value - break - case 'max': - baseSchema.maxLength = item.value - break - case 'min': - baseSchema.minLength = item.value - break - case 'regex': - baseSchema.pattern = item.regex.source - break - } - }) - return merge(baseSchema, zodRef.description ? { description: zodRef.description } : {}, ...schemas) + const baseSchema: SchemaObject = { + type: 'string', + } + const { checks = [] } = zodRef._def + for (const item of checks) { + switch (item.kind) { + case 'email': + baseSchema.format = 'email' + break + case 'uuid': + baseSchema.format = 'uuid' + break + case 'cuid': + baseSchema.format = 'cuid' + break + case 'url': + baseSchema.format = 'uri' + break + case 'datetime': + baseSchema.format = 'date-time' + break + case 'length': + baseSchema.minLength = item.value + baseSchema.maxLength = item.value + break + case 'max': + baseSchema.maxLength = item.value + break + case 'min': + baseSchema.minLength = item.value + break + case 'regex': + baseSchema.pattern = item.regex.source + break + } + } + return merge(baseSchema, zodRef.description ? { description: zodRef.description } : {}, ...schemas) } function parseNumber({ zodRef, schemas }: ParsingArgs): SchemaObject { - const baseSchema: SchemaObject = { - type: 'number', - } - const { checks = [] } = zodRef._def - checks.forEach((item) => { - switch (item.kind) { - case 'max': - baseSchema.maximum = item.value - // TODO: option to make this always explicit? (false instead of non-existent) - if (!item.inclusive) { - ;(baseSchema.exclusiveMaximum as any) = true - } - break - case 'min': - baseSchema.minimum = item.value - if (!item.inclusive) { - ;(baseSchema.exclusiveMinimum as any) = true - } - break - case 'int': - baseSchema.type = 'integer' - break - case 'multipleOf': - baseSchema.multipleOf = item.value - } - }) - return merge(baseSchema, zodRef.description ? { description: zodRef.description } : {}, ...schemas) + const baseSchema: SchemaObject = { + type: 'number', + } + const { checks = [] } = zodRef._def + for (const item of checks) { + switch (item.kind) { + case 'max': + baseSchema.maximum = item.value + // TODO: option to make this always explicit? (false instead of non-existent) + if (!item.inclusive) { + ;(baseSchema.exclusiveMaximum as any) = true + } + break + case 'min': + baseSchema.minimum = item.value + if (!item.inclusive) { + ;(baseSchema.exclusiveMinimum as any) = true + } + break + case 'int': + baseSchema.type = 'integer' + break + case 'multipleOf': + baseSchema.multipleOf = item.value + } + } + return merge(baseSchema, zodRef.description ? { description: zodRef.description } : {}, ...schemas) } function parseObject({ - zodRef, - schemas, - useOutput, + zodRef, + schemas, + useOutput, }: ParsingArgs>): SchemaObject { - let additionalProperties: SchemaObject['additionalProperties'] - - // `catchall` obviates `strict`, `strip`, and `passthrough` - if (!(zodRef._def.catchall instanceof z.ZodNever || zodRef._def.catchall?._def.typeName === 'ZodNever')) { - additionalProperties = generateSchema(zodRef._def.catchall, useOutput) - } else if (zodRef._def.unknownKeys === 'passthrough') { - additionalProperties = true - } else if (zodRef._def.unknownKeys === 'strict') { - additionalProperties = false - } - - // So that `undefined` values don't end up in the schema and be weird - additionalProperties = additionalProperties != null ? { additionalProperties } : {} - - const requiredProperties = Object.keys((zodRef as z.AnyZodObject).shape).filter((key) => { - const item = (zodRef as z.AnyZodObject).shape[key] - return ( - !(item.isOptional() || item instanceof z.ZodDefault || item._def.typeName === 'ZodDefault') && - !(item instanceof z.ZodNever || item._def.typeName === 'ZodDefault') - ) - }) - - const required = requiredProperties.length > 0 ? { required: requiredProperties } : {} - - return merge( - { - type: 'object' as SchemaObjectType, - properties: iterateZodObject({ - zodRef: zodRef as OpenApiZodAnyObject, - schemas, - useOutput, - }), - ...required, - ...additionalProperties, - }, - zodRef.description ? { description: zodRef.description } : {}, - ...schemas, - ) + let additionalProperties: SchemaObject['additionalProperties'] + + // `catchall` obviates `strict`, `strip`, and `passthrough` + if (!(zodRef._def.catchall instanceof z.ZodNever || zodRef._def.catchall?._def.typeName === 'ZodNever')) { + additionalProperties = generateSchema(zodRef._def.catchall, useOutput) + } else if (zodRef._def.unknownKeys === 'passthrough') { + additionalProperties = true + } else if (zodRef._def.unknownKeys === 'strict') { + additionalProperties = false + } + + // So that `undefined` values don't end up in the schema and be weird + additionalProperties = additionalProperties != null ? { additionalProperties } : {} + + const requiredProperties = Object.keys((zodRef as z.AnyZodObject).shape).filter(key => { + const item = (zodRef as z.AnyZodObject).shape[key] + return ( + !(item.isOptional() || item instanceof z.ZodDefault || item._def.typeName === 'ZodDefault') && + !(item instanceof z.ZodNever || item._def.typeName === 'ZodDefault') + ) + }) + + const required = requiredProperties.length > 0 ? { required: requiredProperties } : {} + + return merge( + { + type: 'object' as SchemaObjectType, + properties: iterateZodObject({ + zodRef: zodRef as OpenApiZodAnyObject, + schemas, + useOutput, + }), + ...required, + ...additionalProperties, + }, + zodRef.description ? { description: zodRef.description } : {}, + ...schemas, + ) } function parseRecord({ zodRef, schemas, useOutput }: ParsingArgs): SchemaObject { - return merge( - { - type: 'object' as SchemaObjectType, - additionalProperties: - zodRef._def.valueType instanceof z.ZodUnknown ? {} : generateSchema(zodRef._def.valueType, useOutput), - }, - zodRef.description ? { description: zodRef.description } : {}, - ...schemas, - ) + return merge( + { + type: 'object' as SchemaObjectType, + additionalProperties: + zodRef._def.valueType instanceof z.ZodUnknown ? {} : generateSchema(zodRef._def.valueType, useOutput), + }, + zodRef.description ? { description: zodRef.description } : {}, + ...schemas, + ) } function parseBigInt({ zodRef, schemas }: ParsingArgs): SchemaObject { - return merge( - { type: 'integer' as SchemaObjectType, format: 'int64' }, - zodRef.description ? { description: zodRef.description } : {}, - ...schemas, - ) + return merge( + { type: 'integer' as SchemaObjectType, format: 'int64' }, + zodRef.description ? { description: zodRef.description } : {}, + ...schemas, + ) } function parseBoolean({ zodRef, schemas }: ParsingArgs): SchemaObject { - return merge( - { type: 'boolean' as SchemaObjectType }, - zodRef.description ? { description: zodRef.description } : {}, - ...schemas, - ) + return merge( + { type: 'boolean' as SchemaObjectType }, + zodRef.description ? { description: zodRef.description } : {}, + ...schemas, + ) } function parseDate({ zodRef, schemas }: ParsingArgs): SchemaObject { - return merge( - { type: 'string' as SchemaObjectType, format: 'date-time' }, - zodRef.description ? { description: zodRef.description } : {}, - ...schemas, - ) + return merge( + { type: 'string' as SchemaObjectType, format: 'date-time' }, + zodRef.description ? { description: zodRef.description } : {}, + ...schemas, + ) } function parseNull({ zodRef, schemas }: ParsingArgs): SchemaObject { - return merge( - { - type: 'string' as SchemaObjectType, - format: 'null', - nullable: true, - }, - zodRef.description ? { description: zodRef.description } : {}, - ...schemas, - ) + return merge( + { + type: 'string' as SchemaObjectType, + format: 'null', + nullable: true, + }, + zodRef.description ? { description: zodRef.description } : {}, + ...schemas, + ) } function parseOptionalNullable({ - schemas, - zodRef, - useOutput, + schemas, + zodRef, + useOutput, }: ParsingArgs | z.ZodNullable>): SchemaObject { - return merge( - generateSchema(zodRef.unwrap(), useOutput), - zodRef.description ? { description: zodRef.description } : {}, - ...schemas, - ) + return merge( + generateSchema(zodRef.unwrap(), useOutput), + zodRef.description ? { description: zodRef.description } : {}, + ...schemas, + ) } function parseDefault({ schemas, zodRef, useOutput }: ParsingArgs>): SchemaObject { - return merge( - { - default: zodRef._def.defaultValue(), - ...generateSchema(zodRef._def.innerType, useOutput), - }, - zodRef.description ? { description: zodRef.description } : {}, - ...schemas, - ) + return merge( + { + default: zodRef._def.defaultValue(), + ...generateSchema(zodRef._def.innerType, useOutput), + }, + zodRef.description ? { description: zodRef.description } : {}, + ...schemas, + ) } function parseArray({ schemas, zodRef, useOutput }: ParsingArgs>): SchemaObject { - const constraints: SchemaObject = {} - if (zodRef._def.exactLength != null) { - constraints.minItems = zodRef._def.exactLength.value - constraints.maxItems = zodRef._def.exactLength.value - } - - if (zodRef._def.minLength != null) { - constraints.minItems = zodRef._def.minLength.value - } - if (zodRef._def.maxLength != null) { - constraints.maxItems = zodRef._def.maxLength.value - } - - return merge( - { - type: 'array' as SchemaObjectType, - items: generateSchema(zodRef.element, useOutput), - ...constraints, - }, - zodRef.description ? { description: zodRef.description } : {}, - ...schemas, - ) + const constraints: SchemaObject = {} + if (zodRef._def.exactLength != null) { + constraints.minItems = zodRef._def.exactLength.value + constraints.maxItems = zodRef._def.exactLength.value + } + + if (zodRef._def.minLength != null) { + constraints.minItems = zodRef._def.minLength.value + } + if (zodRef._def.maxLength != null) { + constraints.maxItems = zodRef._def.maxLength.value + } + + return merge( + { + type: 'array' as SchemaObjectType, + items: generateSchema(zodRef.element, useOutput), + ...constraints, + }, + zodRef.description ? { description: zodRef.description } : {}, + ...schemas, + ) } function parseLiteral({ schemas, zodRef }: ParsingArgs>): SchemaObject { - return merge( - { - type: typeof zodRef._def.value as 'string' | 'number' | 'boolean', - enum: [zodRef._def.value], - }, - zodRef.description ? { description: zodRef.description } : {}, - ...schemas, - ) + return merge( + { + type: typeof zodRef._def.value as 'string' | 'number' | 'boolean', + enum: [zodRef._def.value], + }, + zodRef.description ? { description: zodRef.description } : {}, + ...schemas, + ) } function parseEnum({ schemas, zodRef }: ParsingArgs | z.ZodNativeEnum>): SchemaObject { - return merge( - { - type: typeof Object.values(zodRef._def.values)[0] as 'string' | 'number', - enum: Object.values(zodRef._def.values), - }, - zodRef.description ? { description: zodRef.description } : {}, - ...schemas, - ) + return merge( + { + type: typeof Object.values(zodRef._def.values)[0] as 'string' | 'number', + enum: Object.values(zodRef._def.values), + }, + zodRef.description ? { description: zodRef.description } : {}, + ...schemas, + ) } function parseIntersection({ - schemas, - zodRef, - useOutput, + schemas, + zodRef, + useOutput, }: ParsingArgs>): SchemaObject { - return merge( - { - allOf: [generateSchema(zodRef._def.left, useOutput), generateSchema(zodRef._def.right, useOutput)], - }, - zodRef.description ? { description: zodRef.description } : {}, - ...schemas, - ) + return merge( + { + allOf: [generateSchema(zodRef._def.left, useOutput), generateSchema(zodRef._def.right, useOutput)], + }, + zodRef.description ? { description: zodRef.description } : {}, + ...schemas, + ) } function parseUnion({ - schemas, - zodRef, - useOutput, + schemas, + zodRef, + useOutput, }: ParsingArgs>): SchemaObject { - return merge( - { - oneOf: (zodRef as z.ZodUnion<[z.ZodTypeAny, ...z.ZodTypeAny[]]>)._def.options.map((schema) => - generateSchema(schema, useOutput), - ), - }, - zodRef.description ? { description: zodRef.description } : {}, - ...schemas, - ) + return merge( + { + oneOf: (zodRef as z.ZodUnion<[z.ZodTypeAny, ...z.ZodTypeAny[]]>)._def.options.map(schema => + generateSchema(schema, useOutput), + ), + }, + zodRef.description ? { description: zodRef.description } : {}, + ...schemas, + ) } function parseDiscriminatedUnion({ - schemas, - zodRef, - useOutput, + schemas, + zodRef, + useOutput, }: ParsingArgs[]>>): SchemaObject { - return merge( - { - discriminator: { - propertyName: (zodRef as z.ZodDiscriminatedUnion[]>)._def - .discriminator, - }, - oneOf: Array.from( - (zodRef as z.ZodDiscriminatedUnion[]>)._def.options.values(), - ).map((schema) => generateSchema(schema, useOutput)), - }, - zodRef.description ? { description: zodRef.description } : {}, - ...schemas, - ) + return merge( + { + discriminator: { + propertyName: (zodRef as z.ZodDiscriminatedUnion[]>)._def + .discriminator, + }, + oneOf: Array.from( + (zodRef as z.ZodDiscriminatedUnion[]>)._def.options.values(), + ).map(schema => generateSchema(schema, useOutput)), + }, + zodRef.description ? { description: zodRef.description } : {}, + ...schemas, + ) } function parseNever({ zodRef, schemas }: ParsingArgs): SchemaObject { - return merge({ readOnly: true }, zodRef.description ? { description: zodRef.description } : {}, ...schemas) + return merge({ readOnly: true }, zodRef.description ? { description: zodRef.description } : {}, ...schemas) } function catchAllParser({ zodRef, schemas }: ParsingArgs): SchemaObject { - return merge(zodRef.description ? { description: zodRef.description } : {}, ...schemas) + return merge(zodRef.description ? { description: zodRef.description } : {}, ...schemas) } const workerMap = { - ZodObject: parseObject, - ZodRecord: parseRecord, - ZodString: parseString, - ZodNumber: parseNumber, - ZodBigInt: parseBigInt, - ZodBoolean: parseBoolean, - ZodDate: parseDate, - ZodNull: parseNull, - ZodOptional: parseOptionalNullable, - ZodNullable: parseOptionalNullable, - ZodDefault: parseDefault, - ZodArray: parseArray, - ZodLiteral: parseLiteral, - ZodEnum: parseEnum, - ZodNativeEnum: parseEnum, - ZodTransformer: parseTransformation, - ZodEffects: parseTransformation, - ZodIntersection: parseIntersection, - ZodUnion: parseUnion, - ZodDiscriminatedUnion: parseDiscriminatedUnion, - ZodNever: parseNever, - // TODO Transform the rest to schemas - ZodUndefined: catchAllParser, - // TODO: `prefixItems` is allowed in OpenAPI 3.1 which can be used to create tuples - ZodTuple: catchAllParser, - ZodMap: catchAllParser, - ZodFunction: catchAllParser, - ZodLazy: catchAllParser, - ZodPromise: catchAllParser, - ZodAny: catchAllParser, - ZodUnknown: catchAllParser, - ZodVoid: catchAllParser, + ZodObject: parseObject, + ZodRecord: parseRecord, + ZodString: parseString, + ZodNumber: parseNumber, + ZodBigInt: parseBigInt, + ZodBoolean: parseBoolean, + ZodDate: parseDate, + ZodNull: parseNull, + ZodOptional: parseOptionalNullable, + ZodNullable: parseOptionalNullable, + ZodDefault: parseDefault, + ZodArray: parseArray, + ZodLiteral: parseLiteral, + ZodEnum: parseEnum, + ZodNativeEnum: parseEnum, + ZodTransformer: parseTransformation, + ZodEffects: parseTransformation, + ZodIntersection: parseIntersection, + ZodUnion: parseUnion, + ZodDiscriminatedUnion: parseDiscriminatedUnion, + ZodNever: parseNever, + // TODO Transform the rest to schemas + ZodUndefined: catchAllParser, + // TODO: `prefixItems` is allowed in OpenAPI 3.1 which can be used to create tuples + ZodTuple: catchAllParser, + ZodMap: catchAllParser, + ZodFunction: catchAllParser, + ZodLazy: catchAllParser, + ZodPromise: catchAllParser, + ZodAny: catchAllParser, + ZodUnknown: catchAllParser, + ZodVoid: catchAllParser, } type WorkerKeys = keyof typeof workerMap export function generateSchema(zodRef: OpenApiZodAny, useOutput?: boolean): SchemaObject { - const { metaOpenApi = {} } = zodRef - const schemas = [ - zodRef.isNullable && zodRef.isNullable() ? { nullable: true } : {}, - ...(Array.isArray(metaOpenApi) ? metaOpenApi : [metaOpenApi]), - ] - - try { - const typeName = zodRef._def.typeName as WorkerKeys - if (typeName in workerMap) { - return workerMap[typeName]({ - zodRef: zodRef as never, - schemas, - useOutput, - }) - } - - return catchAllParser({ zodRef, schemas }) - } catch (err) { - // eslint-disable-next-line no-console - console.error(err) - return catchAllParser({ zodRef, schemas }) - } + const { metaOpenApi = {} } = zodRef + const schemas = [ + zodRef.isNullable?.() ? { nullable: true } : {}, + ...(Array.isArray(metaOpenApi) ? metaOpenApi : [metaOpenApi]), + ] + + try { + const typeName = zodRef._def.typeName as WorkerKeys + if (typeName in workerMap) { + return workerMap[typeName]({ + zodRef: zodRef as never, + schemas, + useOutput, + }) + } + + return catchAllParser({ zodRef, schemas }) + } catch (err) { + // biome-ignore lint/suspicious/noConsole: no logger available + console.error(err) + return catchAllParser({ zodRef, schemas }) + } } diff --git a/packages/core/src/zodOpenApi/zodOpenApi.test.ts b/packages/core/src/zodOpenApi/zodOpenApi.test.ts index ff508a891..d9d6fb931 100644 --- a/packages/core/src/zodOpenApi/zodOpenApi.test.ts +++ b/packages/core/src/zodOpenApi/zodOpenApi.test.ts @@ -6,833 +6,833 @@ import { extendApi, generateSchema } from './zodOpenApi.impl.js' /* eslint-disable max-len */ // from http://goo.gl/0ejHHW const iso8601 = - /^([+-]?\d{4}(?!\d{2}\b))((-?)((0[1-9]|1[0-2])(\3([12]\d|0[1-9]|3[01]))?|W([0-4]\d|5[0-3])(-?[1-7])?|(00[1-9]|0[1-9]\d|[12]\d{2}|3([0-5]\d|6[1-6])))([T\s]((([01]\d|2[0-3])((:?)[0-5]\d)?|24:?00)([.,]\d+(?!:))?)?(\17[0-5]\d([.,]\d+)?)?([zZ]|([+-])([01]\d|2[0-3]):?([0-5]\d)?)?)?)?$/ + /^([+-]?\d{4}(?!\d{2}\b))((-?)((0[1-9]|1[0-2])(\3([12]\d|0[1-9]|3[01]))?|W([0-4]\d|5[0-3])(-?[1-7])?|(00[1-9]|0[1-9]\d|[12]\d{2}|3([0-5]\d|6[1-6])))([T\s]((([01]\d|2[0-3])((:?)[0-5]\d)?|24:?00)([.,]\d+(?!:))?)?(\17[0-5]\d([.,]\d+)?)?([zZ]|([+-])([01]\d|2[0-3]):?([0-5]\d)?)?)?)?$/ // same as above, except with a strict 'T' separator between date and time const iso8601StrictSeparator = - /^([+-]?\d{4}(?!\d{2}\b))((-?)((0[1-9]|1[0-2])(\3([12]\d|0[1-9]|3[01]))?|W([0-4]\d|5[0-3])(-?[1-7])?|(00[1-9]|0[1-9]\d|[12]\d{2}|3([0-5]\d|6[1-6])))([T]((([01]\d|2[0-3])((:?)[0-5]\d)?|24:?00)([.,]\d+(?!:))?)?(\17[0-5]\d([.,]\d+)?)?([zZ]|([+-])([01]\d|2[0-3]):?([0-5]\d)?)?)?)?$/ + /^([+-]?\d{4}(?!\d{2}\b))((-?)((0[1-9]|1[0-2])(\3([12]\d|0[1-9]|3[01]))?|W([0-4]\d|5[0-3])(-?[1-7])?|(00[1-9]|0[1-9]\d|[12]\d{2}|3([0-5]\d|6[1-6])))([T]((([01]\d|2[0-3])((:?)[0-5]\d)?|24:?00)([.,]\d+(?!:))?)?(\17[0-5]\d([.,]\d+)?)?([zZ]|([+-])([01]\d|2[0-3]):?([0-5]\d)?)?)?)?$/ /* eslint-enable max-len */ const isValidDate = (str: string) => { - // str must have passed the ISO8601 check - // this check is meant to catch invalid dates - // like 2009-02-31 - // first check for ordinal dates - const ordinalMatch = str.match(/^(\d{4})-?(\d{3})([ T]{1}\.*|$)/) - if (ordinalMatch) { - const oYear = Number(ordinalMatch[1]) - const oDay = Number(ordinalMatch[2]) - // if is leap year - if ((oYear % 4 === 0 && oYear % 100 !== 0) || oYear % 400 === 0) { - return oDay <= 366 - } - return oDay <= 365 - } - const match = (str.match(/(\d{4})-?(\d{0,2})-?(\d*)/) as RegExpMatchArray).map(Number) - const year = match[1] - const month = match[2] - const day = match[3] - const monthString = month ? `0${month}`.slice(-2) : month - const dayString = day ? `0${day}`.slice(-2) : day - - // create a date object and compare - const d = new Date(`${year}-${monthString || '01'}-${dayString || '01'}`) - if (month && day) { - return d.getUTCFullYear() === year && d.getUTCMonth() + 1 === month && d.getUTCDate() === day - } - return true + // str must have passed the ISO8601 check + // this check is meant to catch invalid dates + // like 2009-02-31 + // first check for ordinal dates + const ordinalMatch = str.match(/^(\d{4})-?(\d{3})([ T]{1}\.*|$)/) + if (ordinalMatch) { + const oYear = Number(ordinalMatch[1]) + const oDay = Number(ordinalMatch[2]) + // if is leap year + if ((oYear % 4 === 0 && oYear % 100 !== 0) || oYear % 400 === 0) { + return oDay <= 366 + } + return oDay <= 365 + } + const match = (str.match(/(\d{4})-?(\d{0,2})-?(\d*)/) as RegExpMatchArray).map(Number) + const year = match[1] + const month = match[2] + const day = match[3] + const monthString = month ? `0${month}`.slice(-2) : month + const dayString = day ? `0${day}`.slice(-2) : day + + // create a date object and compare + const d = new Date(`${year}-${monthString || '01'}-${dayString || '01'}`) + if (month && day) { + return d.getUTCFullYear() === year && d.getUTCMonth() + 1 === month && d.getUTCDate() === day + } + return true } -export default function isISO8601(str: string, options: any = {}) { - const check = options.strictSeparator ? iso8601StrictSeparator.test(str) : iso8601.test(str) - if (check && options.strict) { - return isValidDate(str) - } - return check +function isISO8601(str: string, options: any = {}) { + const check = options.strictSeparator ? iso8601StrictSeparator.test(str) : iso8601.test(str) + if (check && options.strict) { + return isValidDate(str) + } + return check } describe('zodOpenapi', () => { - /** - * Primitives - */ - it('should support basic primitives', () => { - const zodSchema = extendApi( - z.object({ - aString: z.string().describe('A test string').optional(), - aNumber: z.number().optional(), - aBigInt: z.bigint(), - aBoolean: z.boolean(), - aDate: z.date(), - }), - { - description: `Primitives also testing overwriting of "required"`, - required: ['aNumber'], // All schema settings "merge" - }, - ) - const apiSchema = generateSchema(zodSchema) - - expect(apiSchema).toEqual({ - type: 'object', - properties: { - aString: { description: 'A test string', type: 'string' }, - aNumber: { type: 'number' }, - aBigInt: { type: 'integer', format: 'int64' }, - aBoolean: { type: 'boolean' }, - aDate: { type: 'string', format: 'date-time' }, - }, - required: ['aBigInt', 'aBoolean', 'aDate', 'aNumber'], - description: 'Primitives also testing overwriting of "required"', - }) - }) - - it('should support empty types', () => { - const zodSchema = extendApi( - z.object({ - aUndefined: z.undefined(), - aNull: z.null(), - aVoid: z.void(), - }), - { - description: `Empty types in a schema`, - }, - ) - const apiSchema = generateSchema(zodSchema) - expect(apiSchema).toEqual({ - type: 'object', - properties: { - aUndefined: {}, - aNull: { type: 'string', format: 'null', nullable: true }, - aVoid: {}, - }, - required: ['aNull'], - description: 'Empty types in a schema', - }) - }) - - it('should support proper transform input/output', () => { - const zodTransform = extendApi( - z.string().transform((val) => val.length), - { description: 'Will take in a string, returning the length' }, - ) - const schemaIn = generateSchema(zodTransform) - expect(schemaIn.type).toBe('string') - const schemaOut = generateSchema(zodTransform, true) - expect(schemaOut.type).toBe('number') - }) - - it('should support catch-all types', () => { - const zodSchema = extendApi( - z.object({ - aAny: z.any(), - aUnknown: z.unknown(), - }), - { - description: `Empty types don't belong in schema`, - }, - ) - const apiSchema = generateSchema(zodSchema) - expect(apiSchema.properties).toBeDefined() - expect((apiSchema.properties?.aAny as any).nullable).toBe(true) - expect((apiSchema.properties?.aUnknown as any).nullable).toBe(true) - }) - - it('should support never type', () => { - const zodSchema = extendApi( - z.object({ - aNever: z.never(), - }), - { - description: `Never values! Can not use them!`, - }, - ) - const apiSchema = generateSchema(zodSchema) - expect(apiSchema.properties).toBeDefined() - expect((apiSchema.properties?.aNever as SchemaObject).readOnly).toBe(true) - }) - - it('should support string and string constraints', () => { - const zodSchema = extendApi( - z - .object({ - aStringMax: z.string().max(5), - aStringMin: z.string().min(3), - aStringLength: z.string().length(10), - aStringEmail: z.string().email(), - aStringUrl: z.string().url(), - aStringUUID: z.string().uuid(), - aStringDatetime: z.string().datetime(), - aStringCUID: z.string().cuid(), - aStringRegex: z.string().regex(/^[a-zA-Z]+$/), - aStringNonEmpty: z.string().nonempty(), - }) - .partial(), - { - description: `This is totally unlike String Theory`, - }, - ) - const apiSchema = generateSchema(zodSchema) - expect(apiSchema).toEqual({ - type: 'object', - properties: { - aStringMax: { type: 'string', maxLength: 5 }, - aStringMin: { type: 'string', minLength: 3 }, - aStringLength: { maxLength: 10, minLength: 10, type: 'string' }, - aStringEmail: { type: 'string', format: 'email' }, - aStringUrl: { type: 'string', format: 'uri' }, - aStringUUID: { type: 'string', format: 'uuid' }, - aStringCUID: { type: 'string', format: 'cuid' }, - aStringDatetime: { type: 'string', format: 'date-time' }, - aStringRegex: { type: 'string', pattern: '^[a-zA-Z]+$' }, - aStringNonEmpty: { type: 'string', minLength: 1 }, - }, - description: 'This is totally unlike String Theory', - }) - }) - - it('should support numbers and number constraints', () => { - const zodSchema = extendApi( - z - .object({ - aNumberMin: z.number().min(3), - aNumberMax: z.number().max(8), - aNumberInt: z.number().int(), - aNumberPositive: z.number().positive(), - aNumberNonnegative: z.number().nonnegative(), - aNumberNegative: z.number().negative(), - aNumberNonpositive: z.number().nonpositive(), - aNumberGt: z.number().gt(5), - aNumberLt: z.number().lt(5), - aNumberMultipleOf: z.number().multipleOf(2), - }) - .partial(), - { - description: `Look mah, the horse can count higher than me!`, - }, - ) - const apiSchema = generateSchema(zodSchema) - expect(apiSchema).toEqual({ - type: 'object', - properties: { - aNumberMin: { type: 'number', minimum: 3 }, - aNumberMax: { type: 'number', maximum: 8 }, - aNumberInt: { type: 'integer' }, - aNumberPositive: { type: 'number', minimum: 0, exclusiveMinimum: true }, - aNumberNonnegative: { type: 'number', minimum: 0 }, - aNumberNegative: { type: 'number', maximum: 0, exclusiveMaximum: true }, - aNumberNonpositive: { type: 'number', maximum: 0 }, - aNumberGt: { type: 'number', minimum: 5, exclusiveMinimum: true }, - aNumberLt: { type: 'number', maximum: 5, exclusiveMaximum: true }, - aNumberMultipleOf: { type: 'number', multipleOf: 2 }, - }, - description: 'Look mah, the horse can count higher than me!', - }) - }) - - it('should support arrays and array constraints', () => { - const zodSchema = extendApi( - z - .object({ - aArrayMin: z.array(z.string()).min(3), - aArrayMax: z.array(z.number()).max(8), - aArrayLength: z.array(z.boolean()).length(10), - aArrayNonempty: z.array(z.null()).nonempty(), - aArrayMinAndMax: z.array(z.number()).min(3).max(8), - }) - .partial(), - { - description: 'I need arrays', - }, - ) - const apiSchema = generateSchema(zodSchema) - expect(apiSchema).toEqual({ - type: 'object', - properties: { - aArrayMin: { type: 'array', minItems: 3, items: { type: 'string' } }, - aArrayMax: { type: 'array', maxItems: 8, items: { type: 'number' } }, - aArrayLength: { - type: 'array', - minItems: 10, - maxItems: 10, - items: { type: 'boolean' }, - }, - aArrayNonempty: { - type: 'array', - minItems: 1, - items: { type: 'string', format: 'null', nullable: true }, - }, - aArrayMinAndMax: { - type: 'array', - minItems: 3, - maxItems: 8, - items: { type: 'number' }, - }, - }, - description: 'I need arrays', - }) - }) - - describe('record support', () => { - describe('with a value type', () => { - it('adds the value type to additionalProperties', () => { - const zodSchema = extendApi(z.record(z.number().min(2).max(42)), { - description: 'Record this one for me.', - }) - const apiSchema = generateSchema(zodSchema) - expect(apiSchema).toEqual({ - type: 'object', - additionalProperties: { type: 'number', minimum: 2, maximum: 42 }, - description: 'Record this one for me.', - }) - }) - }) - - describe('with unknown value types', () => { - it('leaves additionalProperties blank', () => { - const zodSchema = extendApi(z.record(z.unknown()), { - description: 'Record this one for me.', - }) - const apiSchema = generateSchema(zodSchema) - expect(apiSchema).toEqual({ - type: 'object', - additionalProperties: {}, - description: 'Record this one for me.', - }) - }) - }) - }) - - it('should support schemas with a default', () => { - const zodSchema = extendApi( - z.object({ - aString: z.string().default('hello'), - aStringWithConstraints: z.string().email().max(100).default('hello'), - aNumber: z.number().default(42), - aNumberWithRestrictions: z.number().min(2).max(42).default(42), - aBoolean: z.boolean().default(false), - nonDefaulted: z.string(), - }), - { - description: 'I defaulted on my debt', - }, - ) - const apiSchema = generateSchema(zodSchema) - expect(apiSchema).toEqual({ - type: 'object', - properties: { - aString: { type: 'string', default: 'hello' }, - aStringWithConstraints: { - type: 'string', - format: 'email', - maxLength: 100, - default: 'hello', - }, - aNumber: { type: 'number', default: 42 }, - aNumberWithRestrictions: { - type: 'number', - minimum: 2, - maximum: 42, - default: 42, - }, - aBoolean: { type: 'boolean', default: false }, - nonDefaulted: { type: 'string' }, - }, - required: ['nonDefaulted'], - description: 'I defaulted on my debt', - }) - }) - - it('should support an object schema that has a default on itself', () => { - const zodSchema = extendApi( - z - .object({ - aString: z.string(), - aNumber: z.number(), - }) - .default({ - aString: 'hello', - aNumber: 42, - }), - { - description: 'I defaulted on my debt', - }, - ) - const apiSchema = generateSchema(zodSchema) - expect(apiSchema).toEqual({ - type: 'object', - properties: { - aString: { type: 'string' }, - aNumber: { type: 'number' }, - }, - default: { - aString: 'hello', - aNumber: 42, - }, - required: ['aString', 'aNumber'], - description: 'I defaulted on my debt', - }) - }) - - it('should support `catchall` on an object schema', () => { - const zodSchema = extendApi( - z - .object({ - aString: z.string(), - aNumber: z.number(), - }) - .catchall( - z.object({ - email: z.string().email(), - available: z.boolean(), - }), - ), - { - description: "Gotta catch 'em all!", - }, - ) - const apiSchema = generateSchema(zodSchema) - expect(apiSchema).toEqual({ - type: 'object', - required: ['aString', 'aNumber'], - properties: { - aString: { type: 'string' }, - aNumber: { type: 'number' }, - }, - additionalProperties: { - type: 'object', - properties: { - email: { type: 'string', format: 'email' }, - available: { type: 'boolean' }, - }, - required: ['email', 'available'], - }, - description: "Gotta catch 'em all!", - }) - }) - - it('should support `passthrough` on an object schema', () => { - const zodSchema = extendApi( - z - .object({ - aString: z.string(), - aNumber: z.number(), - }) - .passthrough(), - { - description: "Gotta catch 'em all!", - }, - ) - const apiSchema = generateSchema(zodSchema) - expect(apiSchema).toEqual({ - type: 'object', - required: ['aString', 'aNumber'], - properties: { - aString: { type: 'string' }, - aNumber: { type: 'number' }, - }, - additionalProperties: true, - description: "Gotta catch 'em all!", - }) - }) - - it('should support `strict` on an object schema', () => { - const zodSchema = extendApi( - z - .object({ - aString: z.string(), - aNumber: z.number(), - }) - .strict(), - { - description: 'Super strict', - }, - ) - const apiSchema = generateSchema(zodSchema) - expect(apiSchema).toEqual({ - type: 'object', - required: ['aString', 'aNumber'], - properties: { - aString: { type: 'string' }, - aNumber: { type: 'number' }, - }, - additionalProperties: false, - description: 'Super strict', - }) - }) - - it('Testing large mixed schema', () => { - enum Fruits { - Apple, - Banana, - } - - const MoreFruits = { - Pear: 'pear', - Plumb: 'plumb', - grapes: 3, - } as const - - const zodSchema = z.object({ - name: extendApi(z.string(), { description: `User full name` }), - email: extendApi(z.string().email().min(4), { - description: 'User email', - }), - whatever: z.string().optional(), - myCollection: extendApi(z.array(z.object({ name: z.string(), count: z.number() })), { maxItems: 10 }), - timeStamp: z.string().refine(isISO8601), - literals: z.object({ - wordOne: z.literal('One').nullable(), - numberTwo: z.literal(2).optional(), - isThisTheEnd: z.literal(false).optional().nullable(), - }), - catchall: z - .object({ - email: z.string().email(), - joined: z.date().optional(), - }) - .catchall(z.object({ name: z.string(), value: z.string() })), - foodTest: extendApi( - z.object({ - fishEnum: extendApi(z.enum(['Salmon', 'Tuna', 'Trout']), { - description: 'Choose your fish', - default: 'Salmon', - }), - fruitEnum: z.nativeEnum(Fruits).default(Fruits.Banana), - moreFruitsEnum: z.nativeEnum(MoreFruits), - }), - { description: 'Have some lunch' }, - ), - employedPerson: extendApi( - z.intersection( - z.object({ name: z.string() }), - z.object({ - role: z.enum(['manager', 'employee', 'intern', 'hopeful']), - }), - ), - { description: 'Our latest addition' }, - ), - makeAChoice: z.union([z.literal('One'), z.literal(2)]), - chooseAPet: z.discriminatedUnion('animal', [ - z.object({ - animal: z.literal('dog'), - theBestQuality: z.literal('fleas'), - }), - z.object({ - animal: z.literal('cat'), - theBestQuality: z.literal('stink'), - }), - ]), - openChoice: extendApi(z.union([z.string(), z.string()]), { - description: 'Odd pattern here', - }), - aNullish: z.string().nullish(), - stringLengthOutput: z.string().transform((val) => val.length), - favourites: z.record(z.object({ name: z.string(), watchCount: z.number() })), - limit: z.number().default(200), - freeform: z - .object({ - name: z.string(), - }) - .passthrough(), - }) - - const schemaTest = generateSchema(zodSchema, true) - - expect(schemaTest).toEqual({ - type: 'object', - properties: { - name: { type: 'string', description: 'User full name' }, - email: { - type: 'string', - format: 'email', - minLength: 4, - description: 'User email', - }, - whatever: { type: 'string' }, - myCollection: { - type: 'array', - items: { - type: 'object', - properties: { name: { type: 'string' }, count: { type: 'number' } }, - required: ['name', 'count'], - }, - maxItems: 10, - }, - timeStamp: { type: 'string' }, - literals: { - type: 'object', - properties: { - wordOne: { nullable: true, type: 'string', enum: ['One'] }, - numberTwo: { type: 'number', enum: [2] }, - isThisTheEnd: { nullable: true, type: 'boolean', enum: [false] }, - }, - required: ['wordOne'], - }, - catchall: { - type: 'object', - properties: { - email: { type: 'string', format: 'email' }, - joined: { type: 'string', format: 'date-time' }, - }, - required: ['email'], - additionalProperties: { - type: 'object', - properties: { - name: { type: 'string' }, - value: { type: 'string' }, - }, - required: ['name', 'value'], - }, - }, - foodTest: { - type: 'object', - properties: { - fishEnum: { - type: 'string', - enum: ['Salmon', 'Tuna', 'Trout'], - description: 'Choose your fish', - default: 'Salmon', - }, - fruitEnum: { - type: 'string', - enum: ['Apple', 'Banana', 0, 1], - default: 1, - }, - moreFruitsEnum: { type: 'string', enum: ['pear', 'plumb', 3] }, - }, - required: ['fishEnum', 'moreFruitsEnum'], - description: 'Have some lunch', - }, - employedPerson: { - allOf: [ - { - type: 'object', - properties: { name: { type: 'string' } }, - required: ['name'], - }, - { - type: 'object', - properties: { - role: { - type: 'string', - enum: ['manager', 'employee', 'intern', 'hopeful'], - }, - }, - required: ['role'], - }, - ], - description: 'Our latest addition', - }, - makeAChoice: { - oneOf: [ - { type: 'string', enum: ['One'] }, - { type: 'number', enum: [2] }, - ], - }, - chooseAPet: { - discriminator: { - propertyName: 'animal', - }, - oneOf: [ - { - properties: { - animal: { - enum: ['dog'], - type: 'string', - }, - theBestQuality: { - enum: ['fleas'], - type: 'string', - }, - }, - required: ['animal', 'theBestQuality'], - type: 'object', - }, - { - properties: { - animal: { - enum: ['cat'], - type: 'string', - }, - theBestQuality: { - enum: ['stink'], - type: 'string', - }, - }, - required: ['animal', 'theBestQuality'], - type: 'object', - }, - ], - }, - openChoice: { - oneOf: [{ type: 'string' }, { type: 'string' }], - description: 'Odd pattern here', - }, - aNullish: { nullable: true, type: 'string' }, - stringLengthOutput: { type: 'number' }, - favourites: { - type: 'object', - additionalProperties: { - type: 'object', - properties: { - name: { type: 'string' }, - watchCount: { type: 'number' }, - }, - required: ['name', 'watchCount'], - }, - }, - limit: { type: 'number', default: 200 }, - freeform: { - type: 'object', - properties: { - name: { type: 'string' }, - }, - additionalProperties: true, - required: ['name'], - }, - }, - required: [ - 'name', - 'email', - 'myCollection', - 'timeStamp', - 'literals', - 'catchall', - 'foodTest', - 'employedPerson', - 'makeAChoice', - 'chooseAPet', - 'openChoice', - 'stringLengthOutput', - 'favourites', - 'freeform', - ], - }) - - // const builder = OpenApiBuilder.create(); - // builder.addSchema('Users', schemaTest); - // builder; - }) - - it('Experimentation', () => { - const UserZ = z.object({ - uid: extendApi(z.string().nonempty(), { - description: 'A firebase generated UUID', - format: 'firebase-uuid', - }), - theme: extendApi(z.enum([`light`, `dark`]), { - description: 'Defaults to light theme', - default: 'light', - }), - email: z.string().email().optional(), - phoneNumber: z.string().min(10).optional(), - }) - - const openApiSchema: SchemaObject = generateSchema(UserZ) - - expect(openApiSchema.properties).toBeDefined() - }) - - it('Take any Zod schema and convert it to an OpenAPI JSON object', () => { - const aZodSchema = z.object({ - uid: z.string().nonempty(), - firstName: z.string().min(2), - lastName: z.string().optional(), - email: z.string().email(), - phoneNumber: z.string().min(10).optional(), - }) - - const myOpenApiSchema = generateSchema(aZodSchema) - - expect(myOpenApiSchema).toEqual({ - type: 'object', - properties: { - uid: { type: 'string', minLength: 1 }, - firstName: { type: 'string', minLength: 2 }, - lastName: { type: 'string' }, - email: { type: 'string', format: 'email' }, - phoneNumber: { type: 'string', minLength: 10 }, - }, - required: ['uid', 'firstName', 'email'], - }) - }) - - it('Extend a Zod schema with additional OpenAPI schema via a function wrapper', () => { - const aZodExtendedSchema = extendApi( - z.object({ - uid: extendApi(z.string().nonempty(), { - title: 'Unique ID', - description: 'A UUID generated by the server', - }), - firstName: z.string().min(2), - lastName: z.string().optional(), - email: z.string().email(), - phoneNumber: extendApi(z.string().min(10), { - description: 'US Phone numbers only', - example: '555-555-5555', - }), - }), - { - title: 'User', - description: 'A user schema', - }, - ) - - const myOpenApiSchema = generateSchema(aZodExtendedSchema) - - expect(myOpenApiSchema).toEqual({ - type: 'object', - properties: { - uid: { - type: 'string', - minLength: 1, - title: 'Unique ID', - description: 'A UUID generated by the server', - }, - firstName: { type: 'string', minLength: 2 }, - lastName: { type: 'string' }, - email: { type: 'string', format: 'email' }, - phoneNumber: { - type: 'string', - minLength: 10, - description: 'US Phone numbers only', - example: '555-555-5555', - }, - }, - required: ['uid', 'firstName', 'email', 'phoneNumber'], - title: 'User', - description: 'A user schema', - }) - }) - - it('merges openapi metadata when extendApi is used more than one', () => { - const timestampSchema = extendApi(z.string(), { - format: 'date-time', - description: 'A timestamp.', - }) - - const newDescription = 'A more specific timestamp.' - - const overriddenSchema = extendApi(timestampSchema, { - description: newDescription, - }) - - const openapiSchema = generateSchema(overriddenSchema) - - expect(openapiSchema).toMatchObject({ - type: 'string', - format: 'date-time', - description: newDescription, - }) - }) - - it('Can cast a string to binary type', () => { - const binarySchema = extendApi(z.string(), { - format: 'binary', - }) - - const openapiSchema = generateSchema(binarySchema) - - expect(openapiSchema).toMatchObject({ - type: 'string', - format: 'binary', - }) - }) + /** + * Primitives + */ + it('should support basic primitives', () => { + const zodSchema = extendApi( + z.object({ + aString: z.string().describe('A test string').optional(), + aNumber: z.number().optional(), + aBigInt: z.bigint(), + aBoolean: z.boolean(), + aDate: z.date(), + }), + { + description: `Primitives also testing overwriting of "required"`, + required: ['aNumber'], // All schema settings "merge" + }, + ) + const apiSchema = generateSchema(zodSchema) + + expect(apiSchema).toEqual({ + type: 'object', + properties: { + aString: { description: 'A test string', type: 'string' }, + aNumber: { type: 'number' }, + aBigInt: { type: 'integer', format: 'int64' }, + aBoolean: { type: 'boolean' }, + aDate: { type: 'string', format: 'date-time' }, + }, + required: ['aBigInt', 'aBoolean', 'aDate', 'aNumber'], + description: 'Primitives also testing overwriting of "required"', + }) + }) + + it('should support empty types', () => { + const zodSchema = extendApi( + z.object({ + aUndefined: z.undefined(), + aNull: z.null(), + aVoid: z.void(), + }), + { + description: 'Empty types in a schema', + }, + ) + const apiSchema = generateSchema(zodSchema) + expect(apiSchema).toEqual({ + type: 'object', + properties: { + aUndefined: {}, + aNull: { type: 'string', format: 'null', nullable: true }, + aVoid: {}, + }, + required: ['aNull'], + description: 'Empty types in a schema', + }) + }) + + it('should support proper transform input/output', () => { + const zodTransform = extendApi( + z.string().transform(val => val.length), + { description: 'Will take in a string, returning the length' }, + ) + const schemaIn = generateSchema(zodTransform) + expect(schemaIn.type).toBe('string') + const schemaOut = generateSchema(zodTransform, true) + expect(schemaOut.type).toBe('number') + }) + + it('should support catch-all types', () => { + const zodSchema = extendApi( + z.object({ + aAny: z.any(), + aUnknown: z.unknown(), + }), + { + description: `Empty types don't belong in schema`, + }, + ) + const apiSchema = generateSchema(zodSchema) + expect(apiSchema.properties).toBeDefined() + expect((apiSchema.properties?.aAny as any).nullable).toBe(true) + expect((apiSchema.properties?.aUnknown as any).nullable).toBe(true) + }) + + it('should support never type', () => { + const zodSchema = extendApi( + z.object({ + aNever: z.never(), + }), + { + description: 'Never values! Can not use them!', + }, + ) + const apiSchema = generateSchema(zodSchema) + expect(apiSchema.properties).toBeDefined() + expect((apiSchema.properties?.aNever as SchemaObject).readOnly).toBe(true) + }) + + it('should support string and string constraints', () => { + const zodSchema = extendApi( + z + .object({ + aStringMax: z.string().max(5), + aStringMin: z.string().min(3), + aStringLength: z.string().length(10), + aStringEmail: z.string().email(), + aStringUrl: z.string().url(), + aStringUUID: z.string().uuid(), + aStringDatetime: z.string().datetime(), + aStringCUID: z.string().cuid(), + aStringRegex: z.string().regex(/^[a-zA-Z]+$/), + aStringNonEmpty: z.string().nonempty(), + }) + .partial(), + { + description: 'This is totally unlike String Theory', + }, + ) + const apiSchema = generateSchema(zodSchema) + expect(apiSchema).toEqual({ + type: 'object', + properties: { + aStringMax: { type: 'string', maxLength: 5 }, + aStringMin: { type: 'string', minLength: 3 }, + aStringLength: { maxLength: 10, minLength: 10, type: 'string' }, + aStringEmail: { type: 'string', format: 'email' }, + aStringUrl: { type: 'string', format: 'uri' }, + aStringUUID: { type: 'string', format: 'uuid' }, + aStringCUID: { type: 'string', format: 'cuid' }, + aStringDatetime: { type: 'string', format: 'date-time' }, + aStringRegex: { type: 'string', pattern: '^[a-zA-Z]+$' }, + aStringNonEmpty: { type: 'string', minLength: 1 }, + }, + description: 'This is totally unlike String Theory', + }) + }) + + it('should support numbers and number constraints', () => { + const zodSchema = extendApi( + z + .object({ + aNumberMin: z.number().min(3), + aNumberMax: z.number().max(8), + aNumberInt: z.number().int(), + aNumberPositive: z.number().positive(), + aNumberNonnegative: z.number().nonnegative(), + aNumberNegative: z.number().negative(), + aNumberNonpositive: z.number().nonpositive(), + aNumberGt: z.number().gt(5), + aNumberLt: z.number().lt(5), + aNumberMultipleOf: z.number().multipleOf(2), + }) + .partial(), + { + description: 'Look mah, the horse can count higher than me!', + }, + ) + const apiSchema = generateSchema(zodSchema) + expect(apiSchema).toEqual({ + type: 'object', + properties: { + aNumberMin: { type: 'number', minimum: 3 }, + aNumberMax: { type: 'number', maximum: 8 }, + aNumberInt: { type: 'integer' }, + aNumberPositive: { type: 'number', minimum: 0, exclusiveMinimum: true }, + aNumberNonnegative: { type: 'number', minimum: 0 }, + aNumberNegative: { type: 'number', maximum: 0, exclusiveMaximum: true }, + aNumberNonpositive: { type: 'number', maximum: 0 }, + aNumberGt: { type: 'number', minimum: 5, exclusiveMinimum: true }, + aNumberLt: { type: 'number', maximum: 5, exclusiveMaximum: true }, + aNumberMultipleOf: { type: 'number', multipleOf: 2 }, + }, + description: 'Look mah, the horse can count higher than me!', + }) + }) + + it('should support arrays and array constraints', () => { + const zodSchema = extendApi( + z + .object({ + aArrayMin: z.array(z.string()).min(3), + aArrayMax: z.array(z.number()).max(8), + aArrayLength: z.array(z.boolean()).length(10), + aArrayNonempty: z.array(z.null()).nonempty(), + aArrayMinAndMax: z.array(z.number()).min(3).max(8), + }) + .partial(), + { + description: 'I need arrays', + }, + ) + const apiSchema = generateSchema(zodSchema) + expect(apiSchema).toEqual({ + type: 'object', + properties: { + aArrayMin: { type: 'array', minItems: 3, items: { type: 'string' } }, + aArrayMax: { type: 'array', maxItems: 8, items: { type: 'number' } }, + aArrayLength: { + type: 'array', + minItems: 10, + maxItems: 10, + items: { type: 'boolean' }, + }, + aArrayNonempty: { + type: 'array', + minItems: 1, + items: { type: 'string', format: 'null', nullable: true }, + }, + aArrayMinAndMax: { + type: 'array', + minItems: 3, + maxItems: 8, + items: { type: 'number' }, + }, + }, + description: 'I need arrays', + }) + }) + + describe('record support', () => { + describe('with a value type', () => { + it('adds the value type to additionalProperties', () => { + const zodSchema = extendApi(z.record(z.number().min(2).max(42)), { + description: 'Record this one for me.', + }) + const apiSchema = generateSchema(zodSchema) + expect(apiSchema).toEqual({ + type: 'object', + additionalProperties: { type: 'number', minimum: 2, maximum: 42 }, + description: 'Record this one for me.', + }) + }) + }) + + describe('with unknown value types', () => { + it('leaves additionalProperties blank', () => { + const zodSchema = extendApi(z.record(z.unknown()), { + description: 'Record this one for me.', + }) + const apiSchema = generateSchema(zodSchema) + expect(apiSchema).toEqual({ + type: 'object', + additionalProperties: {}, + description: 'Record this one for me.', + }) + }) + }) + }) + + it('should support schemas with a default', () => { + const zodSchema = extendApi( + z.object({ + aString: z.string().default('hello'), + aStringWithConstraints: z.string().email().max(100).default('hello'), + aNumber: z.number().default(42), + aNumberWithRestrictions: z.number().min(2).max(42).default(42), + aBoolean: z.boolean().default(false), + nonDefaulted: z.string(), + }), + { + description: 'I defaulted on my debt', + }, + ) + const apiSchema = generateSchema(zodSchema) + expect(apiSchema).toEqual({ + type: 'object', + properties: { + aString: { type: 'string', default: 'hello' }, + aStringWithConstraints: { + type: 'string', + format: 'email', + maxLength: 100, + default: 'hello', + }, + aNumber: { type: 'number', default: 42 }, + aNumberWithRestrictions: { + type: 'number', + minimum: 2, + maximum: 42, + default: 42, + }, + aBoolean: { type: 'boolean', default: false }, + nonDefaulted: { type: 'string' }, + }, + required: ['nonDefaulted'], + description: 'I defaulted on my debt', + }) + }) + + it('should support an object schema that has a default on itself', () => { + const zodSchema = extendApi( + z + .object({ + aString: z.string(), + aNumber: z.number(), + }) + .default({ + aString: 'hello', + aNumber: 42, + }), + { + description: 'I defaulted on my debt', + }, + ) + const apiSchema = generateSchema(zodSchema) + expect(apiSchema).toEqual({ + type: 'object', + properties: { + aString: { type: 'string' }, + aNumber: { type: 'number' }, + }, + default: { + aString: 'hello', + aNumber: 42, + }, + required: ['aString', 'aNumber'], + description: 'I defaulted on my debt', + }) + }) + + it('should support `catchall` on an object schema', () => { + const zodSchema = extendApi( + z + .object({ + aString: z.string(), + aNumber: z.number(), + }) + .catchall( + z.object({ + email: z.string().email(), + available: z.boolean(), + }), + ), + { + description: "Gotta catch 'em all!", + }, + ) + const apiSchema = generateSchema(zodSchema) + expect(apiSchema).toEqual({ + type: 'object', + required: ['aString', 'aNumber'], + properties: { + aString: { type: 'string' }, + aNumber: { type: 'number' }, + }, + additionalProperties: { + type: 'object', + properties: { + email: { type: 'string', format: 'email' }, + available: { type: 'boolean' }, + }, + required: ['email', 'available'], + }, + description: "Gotta catch 'em all!", + }) + }) + + it('should support `passthrough` on an object schema', () => { + const zodSchema = extendApi( + z + .object({ + aString: z.string(), + aNumber: z.number(), + }) + .passthrough(), + { + description: "Gotta catch 'em all!", + }, + ) + const apiSchema = generateSchema(zodSchema) + expect(apiSchema).toEqual({ + type: 'object', + required: ['aString', 'aNumber'], + properties: { + aString: { type: 'string' }, + aNumber: { type: 'number' }, + }, + additionalProperties: true, + description: "Gotta catch 'em all!", + }) + }) + + it('should support `strict` on an object schema', () => { + const zodSchema = extendApi( + z + .object({ + aString: z.string(), + aNumber: z.number(), + }) + .strict(), + { + description: 'Super strict', + }, + ) + const apiSchema = generateSchema(zodSchema) + expect(apiSchema).toEqual({ + type: 'object', + required: ['aString', 'aNumber'], + properties: { + aString: { type: 'string' }, + aNumber: { type: 'number' }, + }, + additionalProperties: false, + description: 'Super strict', + }) + }) + + it('Testing large mixed schema', () => { + enum Fruits { + Apple = 0, + Banana = 1, + } + + const MoreFruits = { + Pear: 'pear', + Plumb: 'plumb', + grapes: 3, + } as const + + const zodSchema = z.object({ + name: extendApi(z.string(), { description: 'User full name' }), + email: extendApi(z.string().email().min(4), { + description: 'User email', + }), + whatever: z.string().optional(), + myCollection: extendApi(z.array(z.object({ name: z.string(), count: z.number() })), { maxItems: 10 }), + timeStamp: z.string().refine(isISO8601), + literals: z.object({ + wordOne: z.literal('One').nullable(), + numberTwo: z.literal(2).optional(), + isThisTheEnd: z.literal(false).optional().nullable(), + }), + catchall: z + .object({ + email: z.string().email(), + joined: z.date().optional(), + }) + .catchall(z.object({ name: z.string(), value: z.string() })), + foodTest: extendApi( + z.object({ + fishEnum: extendApi(z.enum(['Salmon', 'Tuna', 'Trout']), { + description: 'Choose your fish', + default: 'Salmon', + }), + fruitEnum: z.nativeEnum(Fruits).default(Fruits.Banana), + moreFruitsEnum: z.nativeEnum(MoreFruits), + }), + { description: 'Have some lunch' }, + ), + employedPerson: extendApi( + z.intersection( + z.object({ name: z.string() }), + z.object({ + role: z.enum(['manager', 'employee', 'intern', 'hopeful']), + }), + ), + { description: 'Our latest addition' }, + ), + makeAChoice: z.union([z.literal('One'), z.literal(2)]), + chooseAPet: z.discriminatedUnion('animal', [ + z.object({ + animal: z.literal('dog'), + theBestQuality: z.literal('fleas'), + }), + z.object({ + animal: z.literal('cat'), + theBestQuality: z.literal('stink'), + }), + ]), + openChoice: extendApi(z.union([z.string(), z.string()]), { + description: 'Odd pattern here', + }), + aNullish: z.string().nullish(), + stringLengthOutput: z.string().transform(val => val.length), + favourites: z.record(z.object({ name: z.string(), watchCount: z.number() })), + limit: z.number().default(200), + freeform: z + .object({ + name: z.string(), + }) + .passthrough(), + }) + + const schemaTest = generateSchema(zodSchema, true) + + expect(schemaTest).toEqual({ + type: 'object', + properties: { + name: { type: 'string', description: 'User full name' }, + email: { + type: 'string', + format: 'email', + minLength: 4, + description: 'User email', + }, + whatever: { type: 'string' }, + myCollection: { + type: 'array', + items: { + type: 'object', + properties: { name: { type: 'string' }, count: { type: 'number' } }, + required: ['name', 'count'], + }, + maxItems: 10, + }, + timeStamp: { type: 'string' }, + literals: { + type: 'object', + properties: { + wordOne: { nullable: true, type: 'string', enum: ['One'] }, + numberTwo: { type: 'number', enum: [2] }, + isThisTheEnd: { nullable: true, type: 'boolean', enum: [false] }, + }, + required: ['wordOne'], + }, + catchall: { + type: 'object', + properties: { + email: { type: 'string', format: 'email' }, + joined: { type: 'string', format: 'date-time' }, + }, + required: ['email'], + additionalProperties: { + type: 'object', + properties: { + name: { type: 'string' }, + value: { type: 'string' }, + }, + required: ['name', 'value'], + }, + }, + foodTest: { + type: 'object', + properties: { + fishEnum: { + type: 'string', + enum: ['Salmon', 'Tuna', 'Trout'], + description: 'Choose your fish', + default: 'Salmon', + }, + fruitEnum: { + type: 'string', + enum: ['Apple', 'Banana', 0, 1], + default: 1, + }, + moreFruitsEnum: { type: 'string', enum: ['pear', 'plumb', 3] }, + }, + required: ['fishEnum', 'moreFruitsEnum'], + description: 'Have some lunch', + }, + employedPerson: { + allOf: [ + { + type: 'object', + properties: { name: { type: 'string' } }, + required: ['name'], + }, + { + type: 'object', + properties: { + role: { + type: 'string', + enum: ['manager', 'employee', 'intern', 'hopeful'], + }, + }, + required: ['role'], + }, + ], + description: 'Our latest addition', + }, + makeAChoice: { + oneOf: [ + { type: 'string', enum: ['One'] }, + { type: 'number', enum: [2] }, + ], + }, + chooseAPet: { + discriminator: { + propertyName: 'animal', + }, + oneOf: [ + { + properties: { + animal: { + enum: ['dog'], + type: 'string', + }, + theBestQuality: { + enum: ['fleas'], + type: 'string', + }, + }, + required: ['animal', 'theBestQuality'], + type: 'object', + }, + { + properties: { + animal: { + enum: ['cat'], + type: 'string', + }, + theBestQuality: { + enum: ['stink'], + type: 'string', + }, + }, + required: ['animal', 'theBestQuality'], + type: 'object', + }, + ], + }, + openChoice: { + oneOf: [{ type: 'string' }, { type: 'string' }], + description: 'Odd pattern here', + }, + aNullish: { nullable: true, type: 'string' }, + stringLengthOutput: { type: 'number' }, + favourites: { + type: 'object', + additionalProperties: { + type: 'object', + properties: { + name: { type: 'string' }, + watchCount: { type: 'number' }, + }, + required: ['name', 'watchCount'], + }, + }, + limit: { type: 'number', default: 200 }, + freeform: { + type: 'object', + properties: { + name: { type: 'string' }, + }, + additionalProperties: true, + required: ['name'], + }, + }, + required: [ + 'name', + 'email', + 'myCollection', + 'timeStamp', + 'literals', + 'catchall', + 'foodTest', + 'employedPerson', + 'makeAChoice', + 'chooseAPet', + 'openChoice', + 'stringLengthOutput', + 'favourites', + 'freeform', + ], + }) + + // const builder = OpenApiBuilder.create(); + // builder.addSchema('Users', schemaTest); + // builder; + }) + + it('Experimentation', () => { + const UserZ = z.object({ + uid: extendApi(z.string().nonempty(), { + description: 'A firebase generated UUID', + format: 'firebase-uuid', + }), + theme: extendApi(z.enum(['light', 'dark']), { + description: 'Defaults to light theme', + default: 'light', + }), + email: z.string().email().optional(), + phoneNumber: z.string().min(10).optional(), + }) + + const openApiSchema: SchemaObject = generateSchema(UserZ) + + expect(openApiSchema.properties).toBeDefined() + }) + + it('Take any Zod schema and convert it to an OpenAPI JSON object', () => { + const aZodSchema = z.object({ + uid: z.string().nonempty(), + firstName: z.string().min(2), + lastName: z.string().optional(), + email: z.string().email(), + phoneNumber: z.string().min(10).optional(), + }) + + const myOpenApiSchema = generateSchema(aZodSchema) + + expect(myOpenApiSchema).toEqual({ + type: 'object', + properties: { + uid: { type: 'string', minLength: 1 }, + firstName: { type: 'string', minLength: 2 }, + lastName: { type: 'string' }, + email: { type: 'string', format: 'email' }, + phoneNumber: { type: 'string', minLength: 10 }, + }, + required: ['uid', 'firstName', 'email'], + }) + }) + + it('Extend a Zod schema with additional OpenAPI schema via a function wrapper', () => { + const aZodExtendedSchema = extendApi( + z.object({ + uid: extendApi(z.string().nonempty(), { + title: 'Unique ID', + description: 'A UUID generated by the server', + }), + firstName: z.string().min(2), + lastName: z.string().optional(), + email: z.string().email(), + phoneNumber: extendApi(z.string().min(10), { + description: 'US Phone numbers only', + example: '555-555-5555', + }), + }), + { + title: 'User', + description: 'A user schema', + }, + ) + + const myOpenApiSchema = generateSchema(aZodExtendedSchema) + + expect(myOpenApiSchema).toEqual({ + type: 'object', + properties: { + uid: { + type: 'string', + minLength: 1, + title: 'Unique ID', + description: 'A UUID generated by the server', + }, + firstName: { type: 'string', minLength: 2 }, + lastName: { type: 'string' }, + email: { type: 'string', format: 'email' }, + phoneNumber: { + type: 'string', + minLength: 10, + description: 'US Phone numbers only', + example: '555-555-5555', + }, + }, + required: ['uid', 'firstName', 'email', 'phoneNumber'], + title: 'User', + description: 'A user schema', + }) + }) + + it('merges openapi metadata when extendApi is used more than one', () => { + const timestampSchema = extendApi(z.string(), { + format: 'date-time', + description: 'A timestamp.', + }) + + const newDescription = 'A more specific timestamp.' + + const overriddenSchema = extendApi(timestampSchema, { + description: newDescription, + }) + + const openapiSchema = generateSchema(overriddenSchema) + + expect(openapiSchema).toMatchObject({ + type: 'string', + format: 'date-time', + description: newDescription, + }) + }) + + it('Can cast a string to binary type', () => { + const binarySchema = extendApi(z.string(), { + format: 'binary', + }) + + const openapiSchema = generateSchema(binarySchema) + + expect(openapiSchema).toMatchObject({ + type: 'string', + format: 'binary', + }) + }) }) diff --git a/packages/core/test/clientBuilder.test.ts b/packages/core/test/clientBuilder.test.ts new file mode 100644 index 000000000..96249b56b --- /dev/null +++ b/packages/core/test/clientBuilder.test.ts @@ -0,0 +1,60 @@ +import { mkdir, readFile } from 'node:fs/promises' +import { join } from 'node:path' +import { fileURLToPath } from 'node:url' + +import { rimraf } from 'rimraf' + +import { ClientBuilder } from '../src/index.js' + +describe('client-builder', () => { + const workPath = join(fileURLToPath(new URL('.', import.meta.url))) + const outputPath = join(workPath, 'tmp') + + let clientBuilder: ClientBuilder + + beforeEach(async () => { + await rimraf(outputPath) + await mkdir(outputPath) + clientBuilder = new ClientBuilder() + }) + + afterEach(async () => { + await rimraf(outputPath) + await mkdir(outputPath) + clientBuilder.destroy() + }) + + it('can load a config from file', async () => { + clientBuilder.rootPath = workPath + await expect(clientBuilder.loadConfig()).resolves.toBeUndefined() + }) + + it('can write a config file', async () => { + const path = join(outputPath, 'testconf.output.json') + await clientBuilder.writeConfig(path) + const content = await readFile(path) + expect(content.toString('utf-8')).toBeDefined() + }) + + it('can create a HTTP client', async () => { + clientBuilder.rootPath = outputPath + + const defnitions = await clientBuilder.loadDefinitionFiles(join(workPath, 'definitions')) + await clientBuilder.cleanDistFolder() + await clientBuilder.generateHttpClient(defnitions) + await clientBuilder.createIndex() + await clientBuilder.createPackageJson() + clientBuilder.build() + }) + + it('can create a eventbridge client', async () => { + clientBuilder.rootPath = outputPath + + const defnitions = await clientBuilder.loadDefinitionFiles(join(workPath, 'definitions')) + await clientBuilder.cleanDistFolder() + await clientBuilder.generateHEventBridgeClient(defnitions) + await clientBuilder.createIndex() + await clientBuilder.createPackageJson() + clientBuilder.build() + }) +}) diff --git a/packages/core/test/commandInvoke.test.ts b/packages/core/test/commandInvoke.test.ts index 06b664055..d4be929b0 100644 --- a/packages/core/test/commandInvoke.test.ts +++ b/packages/core/test/commandInvoke.test.ts @@ -1,70 +1,66 @@ import { createSandbox } from 'sinon' import { theServiceV1Service } from '../../../test/service/theService/v1/index.js' -import { DefaultEventBridge, EBMessageType } from '../src/index.js' +import { type Command, type CommandErrorResponse, DefaultEventBridge, EBMessageType } from '../src/index.js' describe('command invoke test', () => { - const sandbox = createSandbox() - const eventBridge = new DefaultEventBridge({}) + const sandbox = createSandbox() + const eventBridge = new DefaultEventBridge({}) - let service: any + let service: Awaited> - beforeAll(async () => { - await eventBridge.start() - }) + beforeAll(async () => { + await eventBridge.start() + service = await theServiceV1Service.getInstance(eventBridge, {}) + await service.start() + }) - afterAll(async () => { - await eventBridge.destroy() - }) + afterAll(async () => { + await eventBridge.destroy() + await service.destroy() + sandbox.restore() + }) - beforeAll(async () => { - service = await theServiceV1Service.getInstance(eventBridge) - await service.start() - }) + it('does not fail if schema is matching', async () => { + const payload = 'the payload' + const parameter = 'the parameter' - afterAll(() => { - service.destroy() - sandbox.restore() - }) + const result = await service.executeCommand({ + receiver: { + serviceName: 'theService', + serviceVersion: 'v1', + serviceTarget: 'invokeFoo', + }, + correlationId: '1', + payload: { + payload, + parameter, + }, + } as Readonly) - it('does not fail if schema is matching', async () => { - const payload = 'the payload' - const parameter = 'the parameter' + expect(result.payload).toStrictEqual({ + payload, + parameter, + }) + }) - const result = await service.executeCommand({ - receiver: { - serviceTarget: 'invokeFoo', - }, - correlationId: '1', - payload: { - payload, - parameter, - }, - }) + it('does fail if schema is not matching', async () => { + const payload = 'the payload' + const parameter = 'the parameter' - expect(result.payload).toStrictEqual({ - payload, - parameter, - }) - }) + const result = (await service.executeCommand({ + receiver: { + serviceTarget: 'invokeFooFailed', + }, + correlationId: '1', + payload: { + payload, + parameter, + }, + } as Readonly)) as CommandErrorResponse - it('does fail if schema is not matching', async () => { - const payload = 'the payload' - const parameter = 'the parameter' - - const result = await service.executeCommand({ - receiver: { - serviceTarget: 'invokeFooFailed', - }, - correlationId: '1', - payload: { - payload, - parameter, - }, - }) - - expect(result.isHandledError).toBeFalsy() - expect(result.messageType).toStrictEqual(EBMessageType.CommandErrorResponse) - expect(result.payload.status).toBe(500) - }) + expect(result.isHandledError).toBeFalsy() + expect(result.messageType).toStrictEqual(EBMessageType.CommandErrorResponse) + expect(result.payload.status).toBe(500) + }) }) diff --git a/packages/core/test/definitions/out.json b/packages/core/test/definitions/out.json new file mode 100644 index 000000000..15de5501a --- /dev/null +++ b/packages/core/test/definitions/out.json @@ -0,0 +1,393 @@ +{ + "version": "1.11.0", + "services": { + "Ping": { + "1": { + "description": "Example ping service", + "commands": { + "ping": { + "commandName": "ping", + "commandDescription": "the ping command exposed as http endpoint", + "eventBridgeConfig": { + "durable": false, + "autoacknowledge": true, + "shared": true + }, + "metadata": { + "expose": { + "contentTypeRequest": "application/json", + "contentEncodingRequest": "utf-8", + "contentTypeResponse": "application/json", + "contentEncodingResponse": "utf-8", + "inputPayload": { + "type": "object", + "properties": { + "ping": { + "type": "string", + "title": "Ping input" + } + }, + "required": ["ping"], + "title": "ping input payload schema" + }, + "parameter": { + "type": "object", + "properties": { + "query": { + "type": "string", + "title": "a query parameter" + } + }, + "required": ["query"], + "title": "ping input parameter schema" + }, + "outputPayload": { + "type": "object", + "properties": { + "pong": { + "type": "string", + "title": "Pong output" + } + }, + "required": ["pong"], + "title": "ping output payload schema" + }, + "deprecated": false, + "http": { + "method": "POST", + "path": "ping", + "openApi": { + "description": "the ping command exposed as http endpoint", + "summary": "ping", + "isSecure": false, + "query": [ + { + "name": "query", + "required": false + } + ], + "tags": [], + "additionalStatusCodes": [], + "operationId": "ping" + } + } + } + }, + "eventName": "pinged", + "hooks": { + "beforeGuard": {}, + "afterGuard": {} + }, + "invokes": {}, + "emitList": {} + }, + "foo": { + "commandName": "foo", + "commandDescription": "Calls foo command", + "eventBridgeConfig": { + "durable": false, + "autoacknowledge": true, + "shared": true + }, + "metadata": { + "expose": { + "contentTypeRequest": "application/json", + "contentEncodingRequest": "utf-8", + "contentTypeResponse": "application/json", + "contentEncodingResponse": "utf-8", + "inputPayload": {}, + "parameter": { + "type": "object", + "properties": {}, + "title": "foo input parameter schema" + }, + "outputPayload": { + "type": "object", + "properties": { + "foo": { + "type": "string" + } + }, + "required": ["foo"], + "title": "foo output payload schema" + }, + "deprecated": false, + "http": { + "method": "GET", + "path": "foo", + "openApi": { + "description": "Calls foo command", + "summary": "foo", + "isSecure": true, + "query": [], + "tags": [], + "additionalStatusCodes": [], + "operationId": "foo" + } + } + } + }, + "hooks": { + "beforeGuard": {}, + "afterGuard": {} + }, + "invokes": {}, + "emitList": {} + }, + "paramTest": { + "commandName": "paramTest", + "commandDescription": "Show how to use path parmater and query params", + "eventBridgeConfig": { + "durable": false, + "autoacknowledge": true, + "shared": true + }, + "metadata": { + "expose": { + "contentTypeRequest": "application/json", + "contentEncodingRequest": "utf-8", + "contentTypeResponse": "application/json", + "contentEncodingResponse": "utf-8", + "inputPayload": { + "title": "paramTest input payload schema" + }, + "parameter": { + "type": "object", + "properties": { + "optionalQuery": { + "type": "string", + "example": "optional" + }, + "requiredQuery": { + "type": "string", + "example": "required" + }, + "requiredParam": { + "type": "string", + "example": "required_id" + }, + "optionalParam": { + "type": "string", + "example": "optionalParam" + } + }, + "required": ["requiredQuery", "requiredParam"], + "title": "paramTest input parameter schema" + }, + "outputPayload": { + "type": "object", + "properties": { + "parameter": { + "type": "object", + "properties": { + "optionalQuery": { + "type": "string", + "example": "optional" + }, + "requiredQuery": { + "type": "string", + "example": "required" + }, + "requiredParam": { + "type": "string", + "example": "required_id" + }, + "optionalParam": { + "type": "string", + "example": "optionalParam" + } + }, + "required": ["requiredQuery", "requiredParam"] + } + }, + "required": ["parameter"], + "title": "paramTest output payload schema" + }, + "deprecated": false, + "http": { + "method": "GET", + "path": "param/:requiredParam/:optionalParam?", + "openApi": { + "description": "Show how to use path parmater and query params", + "summary": "paramTest", + "isSecure": false, + "query": [ + { + "name": "optionalQuery", + "required": false + }, + { + "name": "requiredQuery", + "required": true + } + ], + "tags": [], + "additionalStatusCodes": [], + "operationId": "paramTest" + } + } + } + }, + "hooks": { + "beforeGuard": {}, + "afterGuard": {} + }, + "invokes": {}, + "emitList": {} + }, + "delete": { + "commandName": "delete", + "commandDescription": "provide a dummy command", + "eventBridgeConfig": { + "durable": false, + "autoacknowledge": true, + "shared": true + }, + "metadata": { + "expose": { + "contentTypeRequest": "application/json", + "contentEncodingRequest": "utf-8", + "contentTypeResponse": "application/json", + "contentEncodingResponse": "utf-8", + "inputPayload": { + "nullable": true, + "title": "delete input payload schema" + }, + "parameter": { + "type": "object", + "properties": {}, + "title": "delete input parameter schema" + }, + "outputPayload": { + "title": "put output payload schema" + }, + "deprecated": false, + "http": { + "method": "DELETE", + "path": "delete", + "openApi": { + "description": "provide a dummy command", + "summary": "delete", + "isSecure": true, + "query": [], + "tags": [], + "additionalStatusCodes": [], + "operationId": "delete" + } + } + } + }, + "hooks": { + "beforeGuard": {}, + "afterGuard": {} + }, + "invokes": {}, + "emitList": {} + } + }, + "subscriptions": { + "log": { + "subscriptionName": "log", + "subscriptionDescription": "logs the ping events", + "eventBridgeConfig": { + "durable": true, + "autoacknowledge": false, + "shared": true + }, + "metadata": { + "expose": { + "contentTypeRequest": "application/json", + "contentEncodingRequest": "utf-8", + "inputPayload": { + "type": "object", + "properties": { + "pong": { + "type": "string", + "title": "Pong" + } + }, + "required": ["pong"], + "title": "pong payload schema" + } + } + }, + "sender": {}, + "eventName": "pinged", + "hooks": { + "beforeGuard": {}, + "afterGuard": {} + }, + "invokes": {}, + "emitList": {} + } + } + } + }, + "Delay": { + "1": { + "description": "Example to show a service which starts after webserver and registers commands", + "commands": { + "fooBar": { + "commandName": "fooBar", + "commandDescription": "Example for an exposed command", + "eventBridgeConfig": { + "durable": false, + "autoacknowledge": true, + "shared": true + }, + "metadata": { + "expose": { + "contentTypeRequest": "application/json", + "contentEncodingRequest": "utf-8", + "contentTypeResponse": "application/json", + "contentEncodingResponse": "utf-8", + "inputPayload": { + "nullable": true, + "title": "fooBar input payload schema" + }, + "parameter": { + "type": "object", + "properties": { + "p": { + "type": "string" + }, + "q": { + "type": "string" + } + }, + "required": ["p"], + "title": "fooBar input parameter schema" + }, + "outputPayload": { + "nullable": true, + "title": "fooBar output payload schema" + }, + "deprecated": false, + "http": { + "method": "GET", + "path": "foo-bar/:p/:q?", + "openApi": { + "description": "Example for an exposed command", + "summary": "fooBar", + "isSecure": false, + "query": [], + "tags": [], + "additionalStatusCodes": [], + "operationId": "fooBar" + } + } + } + }, + "hooks": { + "beforeGuard": {}, + "afterGuard": {} + }, + "invokes": {}, + "emitList": {} + } + }, + "subscriptions": {} + } + } + } +} diff --git a/packages/core/test/integration.test.ts b/packages/core/test/integration.test.ts index 3610686f5..a37d35faf 100644 --- a/packages/core/test/integration.test.ts +++ b/packages/core/test/integration.test.ts @@ -1,491 +1,584 @@ -import { fail } from 'assert' +import { fail } from 'node:assert' import { createSandbox } from 'sinon' +import { vi } from 'vitest' import { z } from 'zod' import type { ServiceInfoType } from '../src/index.js' -import { DefaultEventBridge, EBMessageType, safeBind, ServiceBuilder, StatusCode } from '../src/index.js' -import { - getCommandMessageMock, - getCommandTransformContextMock, - getEventBridgeMock, - getLoggerMock, - getSubscriptionTransformContextMock, -} from '../src/mocks/index.js' +import { DefaultEventBridge, EBMessageType, ServiceBuilder, StatusCode, safeBind } from '../src/index.js' +import { getCommandMessageMock, getEventBridgeMock, getLoggerMock } from '../src/mocks/index.js' describe('integration test', () => { - const sandbox = createSandbox() - - const beforeCommandGuardHookStub = sandbox.stub() - const afterCommandGuardHookStub = sandbox.stub() - - const beforeSubscriptionGuardHookStub = sandbox.stub() - const afterSubscriptionGuardHookStub = sandbox.stub() - - const serviceOneInfo = { - serviceName: 'ServiceOne', - serviceVersion: '1', - serviceDescription: 'service one description', - } as const satisfies ServiceInfoType - - const serviceTwoInfo = { - serviceName: 'ServiceTwo', - serviceVersion: '1', - serviceDescription: 'service two description', - } as const satisfies ServiceInfoType - - const commandOnePayloadSchema = z.object({ - input: z.string(), - }) - const commandParameterSchema = z.object({ - param: z.number(), - }) - const commandOneOutputSchema = z.object({ - output: z.object({ - commandOne: z.string(), - commandTwo: z.string(), - }), - }) - - const commandTwoPayloadSchema = z.object({ - input: z.string(), - }) - - const commandTwoOutputSchema = z.object({ - output: z.string(), - }) - - const subscriptionOneSchema = z.object({ - result: z.string(), - }) - - type CommandOnePayload = z.input - type CommandTwoPayload = z.input - - const serviceOneBuilder = new ServiceBuilder(serviceOneInfo) - - const serviceOneSchema = z.object({ - optionOne: z.string(), - }) - - serviceOneBuilder.setConfigSchema(serviceOneSchema).setDefaultConfig({ optionOne: 'option one' }) - - const serviceTwoBuilder = new ServiceBuilder(serviceTwoInfo) - - const serviceTwoSchema = z.object({ - optionTwo: z.string(), - }) - - serviceTwoBuilder.setConfigSchema(serviceTwoSchema).setDefaultConfig({ optionTwo: 'option two' }) - - afterAll(() => { - sandbox.restore() - }) - - describe('creates a command for service one', () => { - const commandOneDefinitionBuilder = serviceOneBuilder - .getCommandBuilder('commandOne', 'command one at service one') - .setSuccessEventName('commandOneEmitted') - .addPayloadSchema(commandOnePayloadSchema) - .addParameterSchema(commandParameterSchema) - .addOutputSchema(commandOneOutputSchema) - .setTransformInput(z.string(), commandParameterSchema, async function (context, payload, parameter) { - const parsed = JSON.parse(payload) - - await context.startActiveSpan('activeSpan', {}, undefined, async () => { - context.logger.debug('activeSpan') - }) - - await context.wrapInSpan('wrapInSpan', {}, async () => { - context.logger.debug('wrapInSpan') - }) - - return { - payload: parsed, - parameter, - } - }) - .setTransformOutput(z.string(), async (context, payload, _parameter) => { - await context.startActiveSpan('activeSpan', {}, undefined, async () => { - context.logger.debug('activeSpan') - }) - - await context.wrapInSpan('wrapInSpan', {}, async () => { - context.logger.debug('wrapInSpan') - }) - - return JSON.stringify(payload) - }) - .setBeforeGuardHooks({ - first: async (_context, payload, parameter) => { - beforeCommandGuardHookStub(payload, parameter) - }, - }) - .setAfterGuardHooks({ - some: async (_context, result, payload, parameter) => { - afterCommandGuardHookStub(result, result, payload, parameter) - }, - }) - .exposeAsHttpEndpoint('POST', 'command-one') - .setOpenApiSummary('more description') - .setOpenApiOperationId('command-one') - .addOpenApiErrorStatusCodes(StatusCode.Unauthorized) - .addOpenApiTags('public') - .disableHttpSecurity() - .enableHttpSecurity() - .addQueryParameters({ required: false, name: 'param' }) - .markAsDeprecated() - .canInvoke( - serviceTwoInfo.serviceName, - serviceTwoInfo.serviceVersion, - 'commandTwo', - commandTwoOutputSchema, - commandTwoPayloadSchema, - commandParameterSchema, - ) - .setCommandFunction(async (context, payload, parameter) => { - const invokePayload: CommandTwoPayload = { - input: 'input', - } - - context.logger.debug('call commandTwo') - - const activeSpanResult = await context.startActiveSpan('test', {}, undefined, async () => { - return 'activeSpanResult' - }) - - expect(activeSpanResult).toBe('activeSpanResult') - - const spanResult = await context.wrapInSpan('test', {}, async () => { - return 'spanResult' - }) - - expect(spanResult).toBe('spanResult') - - const commandTwoResult = await context.service.ServiceTwo['1'].commandTwo(invokePayload, parameter) - return { - output: { - commandOne: 'RECEIVED:' + payload.input.toUpperCase(), - commandTwo: commandTwoResult.output, - }, - } - }) - - serviceOneBuilder.addCommandDefinition(commandOneDefinitionBuilder.getDefinition()) - - it('can unit test command function', async () => { - const eventBridgeMock = getEventBridgeMock(sandbox) - const serviceLogger = getLoggerMock(sandbox) - - const service = await serviceOneBuilder.getInstance(eventBridgeMock.mock, { logger: serviceLogger.mock }) - - const commandOne = safeBind(commandOneDefinitionBuilder.getCommandFunction(), service) - - const payload = { input: 'my input' } - const parameter = { param: 1 } - - const messagePayload = JSON.stringify(payload) - const messageParameter = parameter - - const invokePayload = 'input for command two' - - const contextMock = commandOneDefinitionBuilder.getCommandContextMock(messagePayload, messageParameter, sandbox) - - contextMock.stubs.service.ServiceTwo['1'].commandTwo.resolves({ output: invokePayload.toUpperCase() }) - - const result = await commandOne(contextMock.mock, payload, parameter) - - expect(result.output.commandOne).toBe('RECEIVED:MY INPUT') - expect(result.output.commandTwo).toStrictEqual(invokePayload.toUpperCase()) - expect(contextMock.stubs.logger.debug.calledWith('call commandTwo')).toBeTruthy() - }) - - it('can unit test command input transform', async () => { - const eventBridgeMock = getEventBridgeMock(sandbox) - const serviceLogger = getLoggerMock(sandbox) - - const service = await serviceOneBuilder.getInstance(eventBridgeMock.mock, { logger: serviceLogger.mock }) - - const transformInput = commandOneDefinitionBuilder.getTransformInputFunction() - if (!transformInput) { - fail(new Error('transform input function not set')) - } - - const payload = { input: 'my input' } - const parameter = { param: 1 } - - const messagePayload = JSON.stringify(payload) - const messageParameter = parameter - - const contextMock = getCommandTransformContextMock(messagePayload, messageParameter, sandbox) - - const result = await safeBind(transformInput, service)(contextMock.mock, messagePayload, messageParameter) - - expect(result.parameter).toStrictEqual(messageParameter) - expect(result.payload).toStrictEqual(payload) - expect(contextMock.stubs.logger.debug.calledWith('activeSpan')).toBeTruthy() - expect(contextMock.stubs.logger.debug.calledWith('wrapInSpan')).toBeTruthy() - }) - - it('can unit test command output transform', async () => { - const eventBridgeMock = getEventBridgeMock(sandbox) - const serviceLogger = getLoggerMock(sandbox) - - const service = await serviceOneBuilder.getInstance(eventBridgeMock.mock, { logger: serviceLogger.mock }) - - const transformOutput = commandOneDefinitionBuilder.getTransformOutputFunction() - if (!transformOutput) { - fail(new Error('transform output function not set')) - } - - const payload = { input: 'my input' } - const parameter = { param: 1 } - - const messagePayload = JSON.stringify(payload) - const messageParameter = parameter - - const contextMock = getCommandTransformContextMock(messagePayload, messageParameter, sandbox) - - const functionResult = { - output: { - commandOne: 'one', - commandTwo: 'two', - }, - } - const result = await safeBind(transformOutput, service)(contextMock.mock, functionResult, parameter) - - expect(result).toStrictEqual(JSON.stringify(functionResult)) - expect(contextMock.stubs.logger.debug.calledWith('activeSpan')).toBeTruthy() - expect(contextMock.stubs.logger.debug.calledWith('wrapInSpan')).toBeTruthy() - }) - }) - - describe('creates a subscription for service one', () => { - const subscriptionOneBuilder = serviceOneBuilder - .getSubscriptionBuilder('subscriptionOne', 'a subscription in service one') - .filterReceivedBy(serviceOneInfo.serviceName, serviceOneInfo.serviceVersion, 'commandOne', undefined) - .filterForMessageType(EBMessageType.Command) - .addPayloadSchema(commandOnePayloadSchema) - .addParameterSchema(commandParameterSchema) - .addOutputSchema('subscriptionOneConsumed', subscriptionOneSchema) - .setBeforeGuardHooks({ - one: async (_context, payload, parameter) => { - beforeSubscriptionGuardHookStub(payload, parameter) - }, - }) - .setAfterGuardHooks({ - two: async (_context, result, payload, parameter) => { - afterSubscriptionGuardHookStub(result, result, payload, parameter) - }, - }) - .setTransformInput(z.string(), commandParameterSchema, async (context, payload, parameter) => { - const response = JSON.parse(payload) as CommandOnePayload - - await context.startActiveSpan('activeSpan', {}, undefined, async () => { - context.logger.debug('activeSpan') - }) - - await context.wrapInSpan('wrapInSpan', {}, async () => { - context.logger.debug('wrapInSpan') - }) - - return { - payload: response, - parameter, - } - }) - .setTransformOutput(z.string(), async (context, payload, _parameter) => { - await context.startActiveSpan('activeSpan', {}, undefined, async () => { - context.logger.debug('activeSpan') - }) - - await context.wrapInSpan('wrapInSpan', {}, async () => { - context.logger.debug('wrapInSpan') - }) - - return JSON.stringify(payload) - }) - .setSubscriptionFunction(async (context, payload, _parameter) => { - context.logger.debug('subscription one') - return { - result: 'SUBSCRIPTION:' + payload.input.toUpperCase(), - } - }) - - serviceOneBuilder.addSubscriptionDefinition(subscriptionOneBuilder.getDefinition()) - - it('can unit test subscription function', async () => { - const eventBridgeMock = getEventBridgeMock(sandbox) - const serviceLogger = getLoggerMock(sandbox) - - const service = await serviceOneBuilder.getInstance(eventBridgeMock.mock, { logger: serviceLogger.mock }) - - const subscriptionOne = safeBind(subscriptionOneBuilder.getSubscriptionFunction(), service) - - const payload = { input: 'my input' } - const parameter = { param: 1 } - - const message = getCommandMessageMock({ - principalId: 'unit-test', - payload: { - payload: JSON.stringify(payload), - parameter, - }, - }) - - const contextMock = subscriptionOneBuilder.getSubscriptionContextMock(message, sandbox) - - const result = await subscriptionOne(contextMock.mock, payload, parameter) - - expect(result.result).toBe('SUBSCRIPTION:MY INPUT') - expect(contextMock.stubs.logger.debug.calledWith('subscription one')).toBeTruthy() - }) - - it('can unit test subscription transform input function', async () => { - const eventBridgeMock = getEventBridgeMock(sandbox) - const serviceLogger = getLoggerMock(sandbox) - - const service = await serviceOneBuilder.getInstance(eventBridgeMock.mock, { logger: serviceLogger.mock }) - - const transformInput = subscriptionOneBuilder.getTransformInputFunction() - if (!transformInput) { - fail(new Error('transform input function not set')) - } - - const payload = { input: 'my input' } - const parameter = { param: 1 } - - const message = getCommandMessageMock({ - principalId: 'unit-test', - payload: { - payload: JSON.stringify(payload), - parameter, - }, - }) - - const contextMock = getSubscriptionTransformContextMock(message, sandbox) - - const result = await safeBind(transformInput, service)( - contextMock.mock, - message.payload.payload, - message.payload.parameter, - ) - - expect(result.parameter).toStrictEqual(message.payload.parameter) - expect(result.payload).toStrictEqual(payload) - expect(contextMock.stubs.logger.debug.calledWith('activeSpan')).toBeTruthy() - expect(contextMock.stubs.logger.debug.calledWith('wrapInSpan')).toBeTruthy() - }) - - it('can unit test subscription transform output function', async () => { - const eventBridgeMock = getEventBridgeMock(sandbox) - const serviceLogger = getLoggerMock(sandbox) - - const service = await serviceOneBuilder.getInstance(eventBridgeMock.mock, { logger: serviceLogger.mock }) - - const transformOutput = subscriptionOneBuilder.getTransformOutputFunction() - if (!transformOutput) { - fail(new Error('transform output function not set')) - } - - const payload = { input: 'my input' } - const parameter = { param: 1 } - - const message = getCommandMessageMock({ - principalId: 'unit-test', - payload: { - payload: JSON.stringify(payload), - parameter, - }, - }) - - const contextMock = getSubscriptionTransformContextMock(message, sandbox) - - const functionResult = { - result: 'SUBSCRIPTION:MY INPUT', - } - const result = await safeBind(transformOutput, service)(contextMock.mock, functionResult, parameter) - - expect(result).toStrictEqual(JSON.stringify(functionResult)) - expect(contextMock.stubs.logger.debug.calledWith('activeSpan')).toBeTruthy() - expect(contextMock.stubs.logger.debug.calledWith('wrapInSpan')).toBeTruthy() - }) - }) - - it('creates a command for service two', () => { - const commandTwoDefinitionBuilder = serviceTwoBuilder - .getCommandBuilder('commandTwo', 'command two at service two', 'commandTwoCalled') - .addPayloadSchema(commandTwoPayloadSchema) - .addParameterSchema(commandParameterSchema) - .addOutputSchema(commandTwoOutputSchema) - .setCommandFunction(async (_context, payload, _parameter) => { - return { - output: payload.input.toUpperCase(), - } - }) - - serviceTwoBuilder.addCommandDefinition(commandTwoDefinitionBuilder.getDefinition()) - expect(true).toBeTruthy() - }) - - it('creates a subscription for service two', () => { - const subscriptionTwoDefinitionBuilder = serviceTwoBuilder - .getSubscriptionBuilder('subscriptionTwo', 'subscription two at service two') - .subscribeToEvent('subscriptionOneConsumed', '1') - .addPayloadSchema(z.string()) - .setSubscriptionFunction(async (_context, _payload, _parameter) => {}) - - serviceTwoBuilder.addSubscriptionDefinition(subscriptionTwoDefinitionBuilder.getDefinition()) - expect(true).toBeTruthy() - }) - - it('works with default event bridge', async () => { - // jest.useFakeTimers() - - const logger = getLoggerMock(sandbox) - const eventBridge = new DefaultEventBridge({ logger: logger.mock }) - await eventBridge.start() - - const serviceOne = await serviceOneBuilder.getInstance(eventBridge, { logger: getLoggerMock().mock }) - await serviceOne.start() - - const serviceTwo = await serviceTwoBuilder.getInstance(eventBridge, { logger: getLoggerMock().mock }) - await serviceTwo.start() - - const message = getCommandMessageMock({ - receiver: { - serviceName: serviceOneInfo.serviceName, - serviceVersion: serviceOneInfo.serviceVersion, - serviceTarget: 'commandOne', - }, - principalId: 'live-test', - payload: { - payload: '{"input":"one"}', - parameter: { - param: 1, - }, - }, - }) - - const result = await eventBridge.invoke(message) - - await expect(eventBridge.isReady()).toBeTruthy() - await expect(eventBridge.isHealthy()).toBeTruthy() - - await serviceOne.destroy() - await serviceTwo.destroy() - await eventBridge.destroy() - - // jest.runAllTimers() - - expect(result).toBe('{"output":{"commandOne":"RECEIVED:ONE","commandTwo":"INPUT"}}') - expect(logger.stubs.fatal.called).toBeFalsy() - expect(logger.stubs.error.called).toBeFalsy() - expect(logger.stubs.warn.called).toBeFalsy() - - expect(beforeCommandGuardHookStub.called).toBeTruthy() - expect(afterCommandGuardHookStub.called).toBeTruthy() - - expect(beforeSubscriptionGuardHookStub).toBeTruthy() - expect(afterSubscriptionGuardHookStub.called).toBeTruthy() - }) + const sandbox = createSandbox() + + const beforeCommandGuardHookStub = sandbox.stub() + const afterCommandGuardHookStub = sandbox.stub() + + const beforeSubscriptionGuardHookStub = sandbox.stub() + const afterSubscriptionGuardHookStub = sandbox.stub() + + const serviceOneInfo = { + serviceName: 'ServiceOne', + serviceVersion: '1', + serviceDescription: 'service one description', + } as const satisfies ServiceInfoType + + const serviceTwoInfo = { + serviceName: 'ServiceTwo', + serviceVersion: '1', + serviceDescription: 'service two description', + } as const satisfies ServiceInfoType + + const commandOnePayloadSchema = z.object({ + input: z.string(), + }) + const commandParameterSchema = z.object({ + param: z.number(), + }) + const commandOneOutputSchema = z.object({ + output: z.object({ + commandOne: z.string(), + commandTwo: z.string(), + }), + }) + + const commandTwoPayloadSchema = z.object({ + input: z.string(), + }) + + const commandTwoOutputSchema = z.object({ + output: z.string(), + }) + + const subscriptionOneSchema = z.object({ + result: z.string(), + }) + + type CommandOnePayload = z.input + type CommandTwoPayload = z.input + + const serviceOneSchema = z.object({ + optionOne: z.string().default('option one'), + }) + + const serviceOneBuilder = new ServiceBuilder(serviceOneInfo).setConfigSchema(serviceOneSchema).defineResource< + 'resourceOne', + { + getResourceData: () => string + } + >() + + const serviceTwoSchema = z.object({ + optionTwo: z.string().default('option two'), + }) + + const serviceTwoBuilder = new ServiceBuilder(serviceTwoInfo).setConfigSchema(serviceTwoSchema).defineResource< + 'resourceTwo', + { + getResourceData: () => string + } + >() + + afterAll(() => { + vi.restoreAllMocks() + sandbox.restore() + }) + + describe('creates a command for service one', () => { + const commandOneDefinitionBuilder = serviceOneBuilder + .getCommandBuilder('commandOne', 'command one at service one') + .setSuccessEventName('commandOneEmitted') + .addPayloadSchema(commandOnePayloadSchema) + .addParameterSchema(commandParameterSchema) + .addOutputSchema(commandOneOutputSchema) + .setTransformInput(z.string(), commandParameterSchema, async (context, payload, parameter) => { + const parsed = JSON.parse(payload) + + await context.startActiveSpan('activeSpan', {}, undefined, async () => { + context.logger.debug('activeSpan') + }) + + expect(context.resources.resourceOne.getResourceData()).toBe('resourceOneData') + + await context.wrapInSpan('wrapInSpan', {}, async () => { + context.logger.debug('wrapInSpan') + }) + + return { + payload: parsed, + parameter, + } + }) + .setTransformOutput(z.string(), async (context, payload, _parameter) => { + await context.startActiveSpan('activeSpan', {}, undefined, async () => { + context.logger.debug('activeSpan') + }) + + expect(context.resources.resourceOne.getResourceData()).toBe('resourceOneData') + + await context.wrapInSpan('wrapInSpan', {}, async () => { + context.logger.debug('wrapInSpan') + }) + + return JSON.stringify(payload) + }) + .setBeforeGuardHooks({ + first: async (context, payload, parameter) => { + expect(context.resources.resourceOne.getResourceData()).toBe('resourceOneData') + beforeCommandGuardHookStub(payload, parameter) + }, + }) + .setAfterGuardHooks({ + some: async (context, result, payload, parameter) => { + expect(context.resources.resourceOne.getResourceData()).toBe('resourceOneData') + afterCommandGuardHookStub(result, result, payload, parameter) + }, + }) + .exposeAsHttpEndpoint('POST', 'command-one') + .setOpenApiSummary('more description') + .setOpenApiOperationId('command-one') + .addOpenApiErrorStatusCodes(StatusCode.Unauthorized) + .addOpenApiTags('public') + .disableHttpSecurity() + .enableHttpSecurity() + .addQueryParameters({ required: false, name: 'param' }) + .markAsDeprecated() + .canInvoke( + serviceTwoInfo.serviceName, + serviceTwoInfo.serviceVersion, + 'commandTwo', + commandTwoOutputSchema, + commandTwoPayloadSchema, + commandParameterSchema, + ) + .setCommandFunction(async (context, payload, parameter) => { + const invokePayload: CommandTwoPayload = { + input: 'input', + } + + expect(context.resources.resourceOne.getResourceData()).toBe('resourceOneData') + + context.logger.debug('call commandTwo') + + const activeSpanResult = await context.startActiveSpan('test', {}, undefined, async () => { + return 'activeSpanResult' + }) + + expect(activeSpanResult).toBe('activeSpanResult') + + const spanResult = await context.wrapInSpan('test', {}, async () => { + return 'spanResult' + }) + + expect(spanResult).toBe('spanResult') + + const commandTwoResult = await context.service.ServiceTwo['1'].commandTwo(invokePayload, parameter) + return { + output: { + commandOne: `RECEIVED:${payload.input.toUpperCase()}`, + commandTwo: commandTwoResult.output, + }, + } + }) + + serviceOneBuilder.addCommandDefinition(commandOneDefinitionBuilder.getDefinition()) + + it('can unit test command function', async () => { + const eventBridgeMock = getEventBridgeMock(sandbox) + const serviceLogger = getLoggerMock(sandbox) + + const service = await serviceOneBuilder.getInstance(eventBridgeMock.mock, { + logger: serviceLogger.mock, + resources: { + resourceOne: { getResourceData: () => 'resourceOneData' }, + }, + serviceConfig: {}, + }) + + const commandOne = safeBind(commandOneDefinitionBuilder.getCommandFunction(), service) + + const payload = { input: 'my input' } + const parameter = { param: 1 } + + const messagePayload = JSON.stringify(payload) + const messageParameter = parameter + + const invokePayload = 'input for command two' + + const contextMock = commandOneDefinitionBuilder.getCommandContextMock({ + payload: messagePayload, + parameter: messageParameter, + resources: { + resourceOne: { getResourceData: () => 'resourceOneData' }, + }, + sandbox, + }) + + contextMock.stubs.service.ServiceTwo['1'].commandTwo.resolves({ + output: invokePayload.toUpperCase(), + }) + + const result = await commandOne(contextMock.mock, payload, parameter) + + expect(result.output.commandOne).toBe('RECEIVED:MY INPUT') + expect(result.output.commandTwo).toStrictEqual(invokePayload.toUpperCase()) + expect(contextMock.stubs.logger.debug.calledWith('call commandTwo')).toBeTruthy() + }) + + it('can unit test command input transform', async () => { + const eventBridgeMock = getEventBridgeMock(sandbox) + const serviceLogger = getLoggerMock(sandbox) + + const service = await serviceOneBuilder.getInstance(eventBridgeMock.mock, { + logger: serviceLogger.mock, + resources: { + resourceOne: { getResourceData: () => 'resourceOneData' }, + }, + serviceConfig: {}, + }) + + const transformInput = commandOneDefinitionBuilder.getTransformInputFunction() + if (!transformInput) { + fail(new Error('transform input function not set')) + } + + const payload = { input: 'my input' } + const parameter = { param: 1 } + + const messagePayload = JSON.stringify(payload) + const messageParameter = parameter + + const contextMock = commandOneDefinitionBuilder.getCommandTransformContextMock({ + payload: messagePayload, + parameter: messageParameter, + resources: { + resourceOne: { getResourceData: () => 'resourceOneData' }, + }, + sandbox, + }) + + const result = await safeBind(transformInput, service)(contextMock.mock, messagePayload, messageParameter) + + expect(result.parameter).toStrictEqual(messageParameter) + expect(result.payload).toStrictEqual(payload) + expect(contextMock.stubs.logger.debug.calledWith('activeSpan')).toBeTruthy() + expect(contextMock.stubs.logger.debug.calledWith('wrapInSpan')).toBeTruthy() + }) + + it('can unit test command output transform', async () => { + const eventBridgeMock = getEventBridgeMock(sandbox) + const serviceLogger = getLoggerMock(sandbox) + + const service = await serviceOneBuilder.getInstance(eventBridgeMock.mock, { + logger: serviceLogger.mock, + resources: { + resourceOne: { getResourceData: () => 'resourceOneData' }, + }, + serviceConfig: {}, + }) + + const transformOutput = commandOneDefinitionBuilder.getTransformOutputFunction() + if (!transformOutput) { + fail(new Error('transform output function not set')) + } + + const payload = { input: 'my input' } + const parameter = { param: 1 } + + const messagePayload = JSON.stringify(payload) + const messageParameter = parameter + + const contextMock = commandOneDefinitionBuilder.getCommandTransformContextMock({ + payload: messagePayload, + parameter: messageParameter, + resources: { + resourceOne: { getResourceData: () => 'resourceOneData' }, + }, + sandbox, + }) + + const functionResult = { + output: { + commandOne: 'one', + commandTwo: 'two', + }, + } + const result = await safeBind(transformOutput, service)(contextMock.mock, functionResult, parameter) + + expect(result).toStrictEqual(JSON.stringify(functionResult)) + expect(contextMock.stubs.logger.debug.calledWith('activeSpan')).toBeTruthy() + expect(contextMock.stubs.logger.debug.calledWith('wrapInSpan')).toBeTruthy() + }) + }) + + describe('creates a subscription for service one', () => { + const subscriptionOneBuilder = serviceOneBuilder + .getSubscriptionBuilder('subscriptionOne', 'a subscription in service one') + .filterReceivedBy(serviceOneInfo.serviceName, serviceOneInfo.serviceVersion, 'commandOne', undefined) + .filterForMessageType(EBMessageType.Command) + .addPayloadSchema(commandOnePayloadSchema) + .addParameterSchema(commandParameterSchema) + .addOutputSchema('subscriptionOneConsumed', subscriptionOneSchema) + .setBeforeGuardHooks({ + one: async (_context, payload, parameter) => { + beforeSubscriptionGuardHookStub(payload, parameter) + }, + }) + .setAfterGuardHooks({ + two: async (_context, result, payload, parameter) => { + afterSubscriptionGuardHookStub(result, result, payload, parameter) + }, + }) + .setTransformInput(z.string(), commandParameterSchema, async (context, payload, parameter) => { + const response = JSON.parse(payload) as CommandOnePayload + + await context.startActiveSpan('activeSpan', {}, undefined, async () => { + context.logger.debug('activeSpan') + }) + + await context.wrapInSpan('wrapInSpan', {}, async () => { + context.logger.debug('wrapInSpan') + }) + + return { + payload: response, + parameter, + } + }) + .setTransformOutput(z.string(), async (context, payload, _parameter) => { + await context.startActiveSpan('activeSpan', {}, undefined, async () => { + context.logger.debug('activeSpan') + }) + + await context.wrapInSpan('wrapInSpan', {}, async () => { + context.logger.debug('wrapInSpan') + }) + + return JSON.stringify(payload) + }) + .setSubscriptionFunction(async (context, payload, _parameter) => { + context.logger.debug('subscription one') + return { + result: `SUBSCRIPTION:${payload.input.toUpperCase()}`, + } + }) + + serviceOneBuilder.addSubscriptionDefinition(subscriptionOneBuilder.getDefinition()) + + it('can unit test subscription function', async () => { + const eventBridgeMock = getEventBridgeMock(sandbox) + const serviceLogger = getLoggerMock(sandbox) + + const service = await serviceOneBuilder.getInstance(eventBridgeMock.mock, { + logger: serviceLogger.mock, + resources: { + resourceOne: { getResourceData: () => 'resourceOneData' }, + }, + serviceConfig: {}, + }) + + const subscriptionOne = safeBind(subscriptionOneBuilder.getSubscriptionFunction(), service) + + const payload = { input: 'my input' } + const parameter = { param: 1 } + + const message = getCommandMessageMock({ + principalId: 'unit-test', + payload: { + payload: JSON.stringify(payload), + parameter, + }, + }) + + const contextMock = subscriptionOneBuilder.getSubscriptionContextMock({ message, sandbox }) + + const result = await subscriptionOne(contextMock.mock, payload, parameter) + + expect(result.result).toBe('SUBSCRIPTION:MY INPUT') + expect(contextMock.stubs.logger.debug.calledWith('subscription one')).toBeTruthy() + }) + + it('can unit test subscription transform input function', async () => { + const eventBridgeMock = getEventBridgeMock(sandbox) + const serviceLogger = getLoggerMock(sandbox) + + const service = await serviceOneBuilder.getInstance(eventBridgeMock.mock, { + logger: serviceLogger.mock, + resources: { + resourceOne: { getResourceData: () => 'resourceOneData' }, + }, + serviceConfig: {}, + }) + + const transformInput = subscriptionOneBuilder.getTransformInputFunction() + if (!transformInput) { + fail(new Error('transform input function not set')) + } + + const payload = { input: 'my input' } + const parameter = { param: 1 } + + const message = getCommandMessageMock({ + principalId: 'unit-test', + payload: { + payload: JSON.stringify(payload), + parameter, + }, + }) + + const contextMock = subscriptionOneBuilder.getSubscriptionTransformContextMock({ + message, + resources: { + resourceOne: { getResourceData: () => 'resourceOneData' }, + }, + sandbox, + }) + + const result = await safeBind(transformInput, service)( + contextMock.mock, + message.payload.payload, + message.payload.parameter, + ) + + expect(result.parameter).toStrictEqual(message.payload.parameter) + expect(result.payload).toStrictEqual(payload) + expect(contextMock.stubs.logger.debug.calledWith('activeSpan')).toBeTruthy() + expect(contextMock.stubs.logger.debug.calledWith('wrapInSpan')).toBeTruthy() + }) + + it('can unit test subscription transform output function', async () => { + const eventBridgeMock = getEventBridgeMock(sandbox) + const serviceLogger = getLoggerMock(sandbox) + + const service = await serviceOneBuilder.getInstance(eventBridgeMock.mock, { + logger: serviceLogger.mock, + resources: { + resourceOne: { getResourceData: () => 'resourceOneData' }, + }, + serviceConfig: {}, + }) + + const transformOutput = subscriptionOneBuilder.getTransformOutputFunction() + if (!transformOutput) { + fail(new Error('transform output function not set')) + } + + const payload = { input: 'my input' } + const parameter = { param: 1 } + + const message = getCommandMessageMock({ + principalId: 'unit-test', + payload: { + payload: JSON.stringify(payload), + parameter, + }, + }) + + const contextMock = subscriptionOneBuilder.getSubscriptionTransformContextMock({ + message, + resources: { + resourceOne: { getResourceData: () => 'resourceOneData' }, + }, + sandbox, + }) + + const functionResult = { + result: 'SUBSCRIPTION:MY INPUT', + } + const result = await safeBind(transformOutput, service)(contextMock.mock, functionResult, parameter) + + expect(result).toStrictEqual(JSON.stringify(functionResult)) + expect(contextMock.stubs.logger.debug.calledWith('activeSpan')).toBeTruthy() + expect(contextMock.stubs.logger.debug.calledWith('wrapInSpan')).toBeTruthy() + }) + }) + + it('creates a command for service two', () => { + const commandTwoDefinitionBuilder = serviceTwoBuilder + .getCommandBuilder('commandTwo', 'command two at service two', 'commandTwoCalled') + .addPayloadSchema(commandTwoPayloadSchema) + .addParameterSchema(commandParameterSchema) + .addOutputSchema(commandTwoOutputSchema) + .setCommandFunction(async (_context, payload, _parameter) => { + return { + output: payload.input.toUpperCase(), + } + }) + + serviceTwoBuilder.addCommandDefinition(commandTwoDefinitionBuilder.getDefinition()) + expect(true).toBeTruthy() + }) + + it('creates a subscription for service two', () => { + const subscriptionTwoDefinitionBuilder = serviceTwoBuilder + .getSubscriptionBuilder('subscriptionTwo', 'subscription two at service two') + .subscribeToEvent('subscriptionOneConsumed', '1') + .addPayloadSchema(z.string()) + .setSubscriptionFunction(async (_context, _payload, _parameter) => {}) + + serviceTwoBuilder.addSubscriptionDefinition(subscriptionTwoDefinitionBuilder.getDefinition()) + expect(true).toBeTruthy() + }) + + it('works with default event bridge', async () => { + vi.useFakeTimers() + + const logger = getLoggerMock(sandbox) + const eventBridge = new DefaultEventBridge({ logger: logger.mock }) + await eventBridge.start() + + const serviceOne = await serviceOneBuilder.getInstance(eventBridge, { + logger: getLoggerMock().mock, + resources: { + resourceOne: { getResourceData: () => 'resourceOneData' }, + }, + serviceConfig: {}, + }) + await serviceOne.start() + + const serviceTwo = await serviceTwoBuilder.getInstance(eventBridge, { + logger: getLoggerMock().mock, + resources: { + resourceTwo: { getResourceData: () => 'resourceTwoData' }, + }, + serviceConfig: {}, + }) + await serviceTwo.start() + + const message = getCommandMessageMock({ + receiver: { + serviceName: serviceOneInfo.serviceName, + serviceVersion: serviceOneInfo.serviceVersion, + serviceTarget: 'commandOne', + }, + principalId: 'live-test', + payload: { + payload: '{"input":"one"}', + parameter: { + param: 1, + }, + }, + }) + + const result = await eventBridge.invoke(message) + + await expect(eventBridge.isReady()).toBeTruthy() + await expect(eventBridge.isHealthy()).toBeTruthy() + + await serviceOne.destroy() + await serviceTwo.destroy() + await eventBridge.destroy() + + vi.runAllTimers() + + expect(result).toBe('{"output":{"commandOne":"RECEIVED:ONE","commandTwo":"INPUT"}}') + expect(logger.stubs.fatal.called).toBeFalsy() + expect(logger.stubs.error.called).toBeFalsy() + expect(logger.stubs.warn.called).toBeFalsy() + + expect(beforeCommandGuardHookStub.called).toBeTruthy() + expect(afterCommandGuardHookStub.called).toBeTruthy() + + expect(beforeSubscriptionGuardHookStub).toBeTruthy() + expect(afterSubscriptionGuardHookStub.called).toBeTruthy() + }) }) diff --git a/packages/core/test/purista.client.json b/packages/core/test/purista.client.json new file mode 100644 index 000000000..216b806a2 --- /dev/null +++ b/packages/core/test/purista.client.json @@ -0,0 +1,10 @@ +{ + "version": "1.11.0", + "definitionPath": "./definitions", + "outputPath": "./tmp/dist", + "httpClient": { + "buildAs": "both", + "clientName": "HttpClient" + }, + "eventBridgeClient": {} +} diff --git a/packages/core/test/resource.test.ts b/packages/core/test/resource.test.ts new file mode 100644 index 000000000..1a4bda5c8 --- /dev/null +++ b/packages/core/test/resource.test.ts @@ -0,0 +1,102 @@ +import { createSandbox } from 'sinon' +import { z } from 'zod' +import { + ServiceBuilder, + type ServiceInfoType, + getCommandSuccessMessageMock, + getEventBridgeMock, + getLoggerMock, + safeBind, +} from '../src/index.js' + +describe('service resource test', () => { + let sandbox = createSandbox() + beforeEach(() => { + sandbox = createSandbox() + }) + + afterEach(() => { + sandbox.restore() + }) + + const serviceOneInfo = { + serviceName: 'ServiceOne', + serviceVersion: '1', + serviceDescription: 'service one description', + } as const satisfies ServiceInfoType + + const serviceOneSchema = z.object({ + optionOne: z.string(), + }) + + class ExampleResource { + methodA() { + return 'works' + } + } + + const serviceBuilder = new ServiceBuilder(serviceOneInfo) + .setConfigSchema(serviceOneSchema) + .defineResource<'exampleA', ExampleResource>() + + const commandBuilder = serviceBuilder + .getCommandBuilder('exampleCommand', 'This is an example command using a resource') + .setCommandFunction(async function (ctx) { + const _conf = this.config.optionOne + return ctx.resources.exampleA.methodA() + }) + + const subscriptionBuilder = serviceBuilder + .getSubscriptionBuilder('exampleSubscription', 'This is an example command using a resource') + .setSubscriptionFunction(async function (ctx) { + const _conf = this.config.optionOne + return ctx.resources.exampleA.methodA() + }) + + it('can provide resources to a command', async () => { + const service = await serviceBuilder.getInstance(getEventBridgeMock(sandbox).mock, { + logger: getLoggerMock(sandbox).mock, + serviceConfig: { optionOne: 'hello' }, + resources: { exampleA: new ExampleResource() }, + }) + + const command = safeBind(commandBuilder.getCommandFunction(), service) + const payload = {} + const parameter = {} + + const context = commandBuilder.getCommandContextMock({ + payload, + parameter, + resources: { exampleA: { methodA: sandbox.stub().returns('mock return') } }, + sandbox, + }) + + const result = await command(context.mock, payload, parameter) + + expect(result).toBe('mock return') + }) + + it('can provide resources to a subscription', async () => { + const service = await serviceBuilder.getInstance(getEventBridgeMock(sandbox).mock, { + logger: getLoggerMock(sandbox).mock, + serviceConfig: { optionOne: 'hello' }, + resources: { exampleA: new ExampleResource() }, + }) + + const subscription = safeBind(subscriptionBuilder.getSubscriptionFunction(), service) + const payload = {} + const parameter = {} + const message = getCommandSuccessMessageMock(payload) + + const context = subscriptionBuilder.getSubscriptionContextMock({ + message, + resources: { exampleA: new ExampleResource() }, + sandbox, + }) + context.stubs.resources.exampleA.methodA = sandbox.stub().returns('mock return') + + const result = await subscription(context.mock, payload, parameter) + + expect(result).toBe('mock return') + }) +}) diff --git a/packages/core/tsconfig.json b/packages/core/tsconfig.json index 1703cd6ca..e12990332 100644 --- a/packages/core/tsconfig.json +++ b/packages/core/tsconfig.json @@ -1,21 +1,12 @@ { - "extends": "../../tsconfig.json", - "compilerOptions": { - "outDir": "./dist", - "declaration": true, - "sourceMap": false, - "declarationMap": true, - "types": [ - "vitest/globals", - "node" - ], - }, - "include": [ - "./src/**/*", - "./test/*", - ], - "exclude": [ - "./**/*.d.ts", - "./dist/*" - ], -} \ No newline at end of file + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "./dist", + "declaration": true, + "sourceMap": false, + "declarationMap": true, + "types": ["vitest/globals", "node"] + }, + "include": ["./src/**/*", "./test/*"], + "exclude": ["./**/*.d.ts", "./dist/*"] +} diff --git a/packages/core/typedoc.json b/packages/core/typedoc.json index 355bf0f98..71c4b2283 100644 --- a/packages/core/typedoc.json +++ b/packages/core/typedoc.json @@ -1,6 +1,5 @@ { - - "extends": ["../../typedoc.base.json"], - "entryPoints": ["src/index.ts"], - "tsconfig": "./tsconfig.json" -} \ No newline at end of file + "extends": ["../../typedoc.base.json"], + "entryPoints": ["src/index.ts"], + "tsconfig": "./tsconfig.json" +} diff --git a/packages/dapr-sdk/jsr.json b/packages/dapr-sdk/jsr.json new file mode 100644 index 000000000..18f58901e --- /dev/null +++ b/packages/dapr-sdk/jsr.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://jsr.io/schema/config-file.v1.json", + "name": "@purista/dapr-sdk", + "version": "1.11.0", + "description": "SDK and helper to run PURISTA services in Dapr", + "keywords": ["purista", "DAPR", "typescript", "javascript"], + "exports": "./dist/esm/index.js", + "publish": { + "include": ["dist/**/*.js", "dist/**/*.d.ts", "README.md", "package.json"], + "exclude": [ + "src", + ".github", + ".vscode", + ".zed", + "!dist", + "!dist/**/*.js", + "!dist/**/*.d.ts", + ".tshy", + ".tshy-build", + "vendor", + "docs", + "typedoc.json", + "..eslintcache", + ".npmignore" + ] + } +} diff --git a/packages/dapr-sdk/package.json b/packages/dapr-sdk/package.json index b3817e148..bc7c30b13 100644 --- a/packages/dapr-sdk/package.json +++ b/packages/dapr-sdk/package.json @@ -1,79 +1,76 @@ { - "name": "@purista/dapr-sdk", - "version": "1.11.0", - "description": "SDK and helper to run PURISTA services in Dapr", - "homepage": "https://purista.dev", - "repository": { - "type": "git", - "url": "git@github.com:sebastianwessel/purista.git" - }, - "author": "Sebastian Wessel", - "license": "ISC", - "directories": { - "lib": "lib/esm/index.js", - "man": "docs", - "example": "example" - }, - "type": "module", - "main": "./dist/commonjs/index.js", - "exports": { - "./package.json": "./package.json", - ".": { - "import": { - "types": "./dist/esm/index.d.ts", - "default": "./dist/esm/index.js" - }, - "require": { - "types": "./dist/commonjs/index.d.ts", - "default": "./dist/commonjs/index.js" - } - } - }, - "files": [ - "dist/**/*" - ], - "publishConfig": { - "access": "public" - }, - "engines": { - "node": ">=18.15" - }, - "scripts": { - "lint": "eslint . --ext .ts,.json --cache . --fix", - "test": "vitest", - "build": "rimraf dist && tshy" - }, - "tshy": { - "exclude": [ - "src/**/*.test.ts" - ], - "exports": { - "./package.json": "./package.json", - ".": "./src/index.ts" - } - }, - "devDependencies": { - "@hono/node-server": "^1.8.0", - "@types/node": "^20.11.17", - "@types/sinon": "^17.0.3", - "hono": "^4.0.4", - "sinon": "^17.0.1", - "tshy": "^1.11.1", - "vitest": "^1.3.0" - }, - "dependencies": { - "@opentelemetry/api": "^1.7.0", - "@opentelemetry/semantic-conventions": "^1.19.0", - "@purista/base-http-bridge": "*", - "@purista/core": "*" - }, - "peerDependencies": { - "@hono/node-server": "^1.4.0" - }, - "peerDependenciesMeta": { - "@hono/node-server": { - "optional": true - } - }, - "types": "./dist/commonjs/index.d.ts" + "name": "@purista/dapr-sdk", + "version": "1.11.0", + "description": "SDK and helper to run PURISTA services in Dapr", + "homepage": "https://purista.dev", + "repository": { + "type": "git", + "url": "git@github.com:puristajs/purista.git" + }, + "author": "Sebastian Wessel", + "license": "ISC", + "directories": { + "lib": "lib/esm/index.js", + "man": "docs", + "example": "example" + }, + "type": "module", + "main": "./dist/commonjs/index.js", + "exports": { + "./package.json": "./package.json", + ".": { + "import": { + "types": "./dist/esm/index.d.ts", + "default": "./dist/esm/index.js" + }, + "require": { + "types": "./dist/commonjs/index.d.ts", + "default": "./dist/commonjs/index.js" + } + } + }, + "files": ["dist/**/*"], + "publishConfig": { + "access": "public" + }, + "engines": { + "node": ">=18.15" + }, + "scripts": { + "lint": "npx @biomejs/biome check --write", + "test": "vitest", + "build": "rimraf dist && tshy" + }, + "tshy": { + "exclude": ["src/**/*.test.ts"], + "exports": { + "./package.json": "./package.json", + ".": "./src/index.ts" + } + }, + "devDependencies": { + "@hono/node-server": "^1.12.2", + "@types/node": "^22.5.1", + "@types/sinon": "^17.0.3", + "hono": "^4.4.7", + "sinon": "^19.0.2", + "tshy": "^3.0.2", + "vitest": "^3.0.4" + }, + "dependencies": { + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/semantic-conventions": "^1.27.0", + "@purista/base-http-bridge": "*", + "@purista/core": "*" + }, + "peerDependencies": { + "@hono/node-server": "^1.12.0" + }, + "peerDependenciesMeta": { + "@hono/node-server": { + "optional": true + } + }, + "types": "./dist/commonjs/index.d.ts", + "module": "./dist/esm/index.js" } diff --git a/packages/dapr-sdk/src/DaprClient/DaprClient.impl.ts b/packages/dapr-sdk/src/DaprClient/DaprClient.impl.ts index 88a847094..575904999 100644 --- a/packages/dapr-sdk/src/DaprClient/DaprClient.impl.ts +++ b/packages/dapr-sdk/src/DaprClient/DaprClient.impl.ts @@ -2,71 +2,71 @@ import { join } from 'node:path' import type { HttpEventBridgeClient } from '@purista/base-http-bridge' import type { - Command, - CommandResponse, - EBMessage, - EBMessageAddress, - EventBridgeConfig, - HttpExposedServiceMeta, + Command, + CommandResponse, + EBMessage, + EBMessageAddress, + EventBridgeConfig, + HttpExposedServiceMeta, } from '@purista/core' -import { convertToKebabCase, HttpClient, StatusCode, UnhandledError } from '@purista/core' +import { HttpClient, StatusCode, UnhandledError, convertToKebabCase } from '@purista/core' import type { DaprEventBridgeConfig } from '../DaprEventBridge/index.js' import { DAPR_API_VERSION } from '../types/index.js' export class DaprClient extends HttpClient> implements HttpEventBridgeClient { - getInternalPathForSubscription(address: EBMessageAddress) { - // [baseUrl]/v1.0/invoke/app-user-v1/method/purista/subscription/[subscription-name] - return join(this.config.pathPrefix ?? 'purista', 'subscription', convertToKebabCase(address.serviceTarget)) - } + getInternalPathForSubscription(address: EBMessageAddress) { + // [baseUrl]/v1.0/invoke/app-user-v1/method/purista/subscription/[subscription-name] + return join(this.config.pathPrefix ?? 'purista', 'subscription', convertToKebabCase(address.serviceTarget)) + } - getInternalPathForCommand(address: EBMessageAddress) { - // [baseUrl]/v1.0/invoke/user-v1/method/purista/command/[command-name] - return join(this.config.pathPrefix ?? 'purista', 'command', convertToKebabCase(address.serviceTarget)) - } + getInternalPathForCommand(address: EBMessageAddress) { + // [baseUrl]/v1.0/invoke/user-v1/method/purista/command/[command-name] + return join(this.config.pathPrefix ?? 'purista', 'command', convertToKebabCase(address.serviceTarget)) + } - getApiPathForCommand(addess: EBMessageAddress, metadata: HttpExposedServiceMeta) { - // [baseUrl]/api/v1/[command expose.http.path] - return join(this.config.apiPrefix ?? 'api', `v${addess.serviceVersion}`, metadata.expose.http.path) - } + getApiPathForCommand(addess: EBMessageAddress, metadata: HttpExposedServiceMeta) { + // [baseUrl]/api/v1/[command expose.http.path] + return join(this.config.apiPrefix ?? 'api', `v${addess.serviceVersion}`, metadata.expose.http.path) + } - async invoke(command: Command, headers?: Record, timeout?: number): Promise { - // [baseUrl]/v1.0/invoke/user-v1/method/purista/command/[commandName] - const path = join( - this.config.clientConfig?.daprApiVersion ?? DAPR_API_VERSION, - 'invoke', - `${this.config.clientConfig?.appPrefix ?? ''}${convertToKebabCase( - command.receiver.serviceName, - )}-v${convertToKebabCase(command.receiver.serviceVersion)}`, - 'method', - this.getInternalPathForCommand(command.receiver), - ) + async invoke(command: Command, headers?: Record, timeout?: number): Promise { + // [baseUrl]/v1.0/invoke/user-v1/method/purista/command/[commandName] + const path = join( + this.config.clientConfig?.daprApiVersion ?? DAPR_API_VERSION, + 'invoke', + `${this.config.clientConfig?.appPrefix ?? ''}${convertToKebabCase( + command.receiver.serviceName, + )}-v${convertToKebabCase(command.receiver.serviceVersion)}`, + 'method', + this.getInternalPathForCommand(command.receiver), + ) - return this.post(path, command, { headers, timeout }) - } + return this.post(path, command, { headers, timeout }) + } - async sendEvent(message: EBMessage, headers?: Record) { - if (!message.eventName) { - throw new UnhandledError(StatusCode.InternalServerError, 'message can not be sent as event - event name not set') - } + async sendEvent(message: EBMessage, headers?: Record) { + if (!message.eventName) { + throw new UnhandledError(StatusCode.InternalServerError, 'message can not be sent as event - event name not set') + } - const path = join( - this.config.clientConfig?.daprApiVersion ?? DAPR_API_VERSION, - 'publish', - this.config.clientConfig?.pubSubName ?? 'pubsub', - message.eventName, - ) + const path = join( + this.config.clientConfig?.daprApiVersion ?? DAPR_API_VERSION, + 'publish', + this.config.clientConfig?.pubSubName ?? 'pubsub', + message.eventName, + ) - await this.post(path, message, { headers }) - } + await this.post(path, message, { headers }) + } - async isSidecarAvailable() { - try { - const path = join(this.config.clientConfig?.daprApiVersion ?? DAPR_API_VERSION, 'metadata') - const result = await this.get(path) - return !!result - } catch (e) { - return false - } - } + async isSidecarAvailable() { + try { + const path = join(this.config.clientConfig?.daprApiVersion ?? DAPR_API_VERSION, 'metadata') + const result = await this.get(path) + return !!result + } catch (e) { + return false + } + } } diff --git a/packages/dapr-sdk/src/DaprClient/daprClient.test.ts b/packages/dapr-sdk/src/DaprClient/daprClient.test.ts index 1db0c2ed9..f5d996b66 100644 --- a/packages/dapr-sdk/src/DaprClient/daprClient.test.ts +++ b/packages/dapr-sdk/src/DaprClient/daprClient.test.ts @@ -6,165 +6,165 @@ import { createSandbox } from 'sinon' import { DaprClient } from './DaprClient.impl.js' describe('DaprClient', () => { - const baseUrl = 'http://example.com' - let sandbox: SinonSandbox - - beforeEach(() => { - sandbox = createSandbox() - }) - - afterEach(() => { - sandbox.restore() - sandbox.reset() - }) - - it('returns a path for getInternalPathForSubscription', () => { - const client = new DaprClient({ - baseUrl, - logger: getLoggerMock(sandbox).mock, - serve: sandbox.stub(), - }) - const address: EBMessageAddress = { - serviceName: 'testServer', - serviceVersion: '1', - serviceTarget: 'exampleSubscription', - } - expect(client.getInternalPathForSubscription(address)).toBe('purista/subscription/example-subscription') - }) - - it('returns a path for getInternalPathForCommand', () => { - const client = new DaprClient({ - baseUrl, - logger: getLoggerMock(sandbox).mock, - serve: sandbox.stub(), - }) - const address: EBMessageAddress = { - serviceName: 'testServer', - serviceVersion: '1', - serviceTarget: 'exampleCommand', - } - expect(client.getInternalPathForCommand(address)).toBe('purista/command/example-command') - }) - - it('returns a path for getApiPathForCommand', () => { - const client = new DaprClient({ - baseUrl, - logger: getLoggerMock(sandbox).mock, - serve: sandbox.stub(), - }) - const address: EBMessageAddress = { - serviceName: 'testServer', - serviceVersion: '1', - serviceTarget: 'exampleCommand', - } - const metadata: HttpExposedServiceMeta = { - expose: { - http: { - method: 'POST', - path: '/example', - }, - }, - } - expect(client.getApiPathForCommand(address, metadata)).toBe('api/v1/example') - }) - - it('can invoke a command', async () => { - const client = new DaprClient({ - baseUrl, - logger: getLoggerMock(sandbox).mock, - serve: sandbox.stub(), - }) - - const response = { example: 'response' } - const command = getCommandMessageMock() - - sandbox.stub(global, 'fetch').callsFake(() => - Promise.resolve({ - headers: { - get: () => 'application/json', - }, - ok: true, - json: () => Promise.resolve(response), - text: () => Promise.resolve(JSON.stringify(response)), - } as any), - ) - await expect(client.invoke(command)).resolves.toBe(response) - }) - - it('can send a event a command', async () => { - const client = new DaprClient({ - baseUrl, - logger: getLoggerMock(sandbox).mock, - serve: sandbox.stub(), - }) - - const response = { example: 'response' } - const command = getCommandMessageMock({ eventName: 'test' }) - - sandbox.stub(global, 'fetch').callsFake(() => - Promise.resolve({ - headers: { - get: () => 'application/json', - }, - ok: true, - json: () => Promise.resolve(response), - text: () => Promise.resolve(JSON.stringify(response)), - } as any), - ) - await expect(client.sendEvent(command)).resolves.toBeUndefined() - }) - - it('throws of no event name is provided', async () => { - const client = new DaprClient({ - baseUrl, - logger: getLoggerMock(sandbox).mock, - serve: sandbox.stub(), - }) - - const command = getCommandMessageMock({ eventName: undefined }) - await expect(client.sendEvent(command)).rejects.toThrowError( - 'message can not be sent as event - event name not set', - ) - }) - - it('returns true if sidecar is available', async () => { - const client = new DaprClient({ - baseUrl, - logger: getLoggerMock(sandbox).mock, - serve: sandbox.stub(), - }) - - sandbox.stub(global, 'fetch').callsFake(() => - Promise.resolve({ - headers: { - get: () => 'application/json', - }, - ok: true, - json: () => Promise.resolve({}), - text: () => Promise.resolve(JSON.stringify({})), - } as any), - ) - await expect(client.isSidecarAvailable()).resolves.toBeTruthy() - }) - - it('returns false if sidecar is not available', async () => { - const client = new DaprClient({ - baseUrl, - logger: getLoggerMock(sandbox).mock, - serve: sandbox.stub(), - }) - - sandbox.stub(global, 'fetch').callsFake(() => - Promise.resolve({ - headers: { - get: () => 'application/json', - }, - ok: true, - json: () => Promise.reject(new Error('unavailable')), - text: () => Promise.reject(new Error('unavailable')), - } as any), - ) - - await expect(client.isSidecarAvailable()).resolves.toBeFalsy() - }) + const baseUrl = 'http://example.com' + let sandbox: SinonSandbox + + beforeEach(() => { + sandbox = createSandbox() + }) + + afterEach(() => { + sandbox.restore() + sandbox.reset() + }) + + it('returns a path for getInternalPathForSubscription', () => { + const client = new DaprClient({ + baseUrl, + logger: getLoggerMock(sandbox).mock, + serve: sandbox.stub(), + }) + const address: EBMessageAddress = { + serviceName: 'testServer', + serviceVersion: '1', + serviceTarget: 'exampleSubscription', + } + expect(client.getInternalPathForSubscription(address)).toBe('purista/subscription/example-subscription') + }) + + it('returns a path for getInternalPathForCommand', () => { + const client = new DaprClient({ + baseUrl, + logger: getLoggerMock(sandbox).mock, + serve: sandbox.stub(), + }) + const address: EBMessageAddress = { + serviceName: 'testServer', + serviceVersion: '1', + serviceTarget: 'exampleCommand', + } + expect(client.getInternalPathForCommand(address)).toBe('purista/command/example-command') + }) + + it('returns a path for getApiPathForCommand', () => { + const client = new DaprClient({ + baseUrl, + logger: getLoggerMock(sandbox).mock, + serve: sandbox.stub(), + }) + const address: EBMessageAddress = { + serviceName: 'testServer', + serviceVersion: '1', + serviceTarget: 'exampleCommand', + } + const metadata: HttpExposedServiceMeta = { + expose: { + http: { + method: 'POST', + path: '/example', + }, + }, + } + expect(client.getApiPathForCommand(address, metadata)).toBe('api/v1/example') + }) + + it('can invoke a command', async () => { + const client = new DaprClient({ + baseUrl, + logger: getLoggerMock(sandbox).mock, + serve: sandbox.stub(), + }) + + const response = { example: 'response' } + const command = getCommandMessageMock() + + sandbox.stub(global, 'fetch').callsFake(() => + Promise.resolve({ + headers: { + get: () => 'application/json', + }, + ok: true, + json: () => Promise.resolve(response), + text: () => Promise.resolve(JSON.stringify(response)), + } as any), + ) + await expect(client.invoke(command)).resolves.toBe(response) + }) + + it('can send a event a command', async () => { + const client = new DaprClient({ + baseUrl, + logger: getLoggerMock(sandbox).mock, + serve: sandbox.stub(), + }) + + const response = { example: 'response' } + const command = getCommandMessageMock({ eventName: 'test' }) + + sandbox.stub(global, 'fetch').callsFake(() => + Promise.resolve({ + headers: { + get: () => 'application/json', + }, + ok: true, + json: () => Promise.resolve(response), + text: () => Promise.resolve(JSON.stringify(response)), + } as any), + ) + await expect(client.sendEvent(command)).resolves.toBeUndefined() + }) + + it('throws of no event name is provided', async () => { + const client = new DaprClient({ + baseUrl, + logger: getLoggerMock(sandbox).mock, + serve: sandbox.stub(), + }) + + const command = getCommandMessageMock({ eventName: undefined }) + await expect(client.sendEvent(command)).rejects.toThrowError( + 'message can not be sent as event - event name not set', + ) + }) + + it('returns true if sidecar is available', async () => { + const client = new DaprClient({ + baseUrl, + logger: getLoggerMock(sandbox).mock, + serve: sandbox.stub(), + }) + + sandbox.stub(global, 'fetch').callsFake(() => + Promise.resolve({ + headers: { + get: () => 'application/json', + }, + ok: true, + json: () => Promise.resolve({}), + text: () => Promise.resolve(JSON.stringify({})), + } as any), + ) + await expect(client.isSidecarAvailable()).resolves.toBeTruthy() + }) + + it('returns false if sidecar is not available', async () => { + const client = new DaprClient({ + baseUrl, + logger: getLoggerMock(sandbox).mock, + serve: sandbox.stub(), + }) + + sandbox.stub(global, 'fetch').callsFake(() => + Promise.resolve({ + headers: { + get: () => 'application/json', + }, + ok: true, + json: () => Promise.reject(new Error('unavailable')), + text: () => Promise.reject(new Error('unavailable')), + } as any), + ) + + await expect(client.isSidecarAvailable()).resolves.toBeFalsy() + }) }) diff --git a/packages/dapr-sdk/src/DaprClient/getDefaultClientConfig.impl.ts b/packages/dapr-sdk/src/DaprClient/getDefaultClientConfig.impl.ts index a1088bf7f..6675a3c08 100644 --- a/packages/dapr-sdk/src/DaprClient/getDefaultClientConfig.impl.ts +++ b/packages/dapr-sdk/src/DaprClient/getDefaultClientConfig.impl.ts @@ -1,13 +1,13 @@ import { DAPR_API_VERSION, DEFAULT_DAPR_HOST, DEFAULT_DAPR_PORT } from '../types/index.js' export const getDefaultClientConfig = () => { - return { - daprHost: process.env.DAPR_HOST ?? DEFAULT_DAPR_HOST, - daprPort: process.env.DAPR_HTTP_PORT ?? DEFAULT_DAPR_PORT, - daprApiToken: undefined, - isKeepAlive: true, - pubSubName: 'pubsub', - daprApiVersion: DAPR_API_VERSION, - appPrefix: 'app-', - } + return { + daprHost: process.env.DAPR_HOST ?? DEFAULT_DAPR_HOST, + daprPort: process.env.DAPR_HTTP_PORT ?? DEFAULT_DAPR_PORT, + daprApiToken: undefined, + isKeepAlive: true, + pubSubName: 'pubsub', + daprApiVersion: DAPR_API_VERSION, + appPrefix: 'app-', + } } diff --git a/packages/dapr-sdk/src/DaprClient/types/DaprClientConfig.ts b/packages/dapr-sdk/src/DaprClient/types/DaprClientConfig.ts index e828f4daa..85820cf5b 100644 --- a/packages/dapr-sdk/src/DaprClient/types/DaprClientConfig.ts +++ b/packages/dapr-sdk/src/DaprClient/types/DaprClientConfig.ts @@ -1,41 +1,41 @@ export type DaprClientConfig = { - /** - * The Dapr api version - * @default v1.0 - */ - daprApiVersion: string - /** - * Host location of the Dapr sidecar. - * @default 127.0.0.1 - */ - daprHost?: string - /** - * Port of the Dapr sidecar. - * @default 3500. - */ - daprPort?: string + /** + * The Dapr api version + * @default v1.0 + */ + daprApiVersion: string + /** + * Host location of the Dapr sidecar. + * @default 127.0.0.1 + */ + daprHost?: string + /** + * Port of the Dapr sidecar. + * @default 3500. + */ + daprPort?: string - /** - * The prefix to generate the app-ID of other services. - * @default `app-` - */ - appPrefix?: string + /** + * The prefix to generate the app-ID of other services. + * @default `app-` + */ + appPrefix?: string - /** - * API token to authenticate with Dapr. - * See https://docs.dapr.io/operations/security/api-token/. - */ - daprApiToken?: string + /** + * API token to authenticate with Dapr. + * See https://docs.dapr.io/operations/security/api-token/. + */ + daprApiToken?: string - /** - * If set to false, the HTTP client will not reuse the same connection for multiple requests. - * @default true - */ - isKeepAlive?: boolean + /** + * If set to false, the HTTP client will not reuse the same connection for multiple requests. + * @default true + */ + isKeepAlive?: boolean - /** - * The PubSub to be used for event messages - * @default pubsub - */ - pubSubName?: string + /** + * The PubSub to be used for event messages + * @default pubsub + */ + pubSubName?: string } diff --git a/packages/dapr-sdk/src/DaprConfigStore/DaprConfigStore.impl.ts b/packages/dapr-sdk/src/DaprConfigStore/DaprConfigStore.impl.ts index 1891f46fe..6c29a9a38 100644 --- a/packages/dapr-sdk/src/DaprConfigStore/DaprConfigStore.impl.ts +++ b/packages/dapr-sdk/src/DaprConfigStore/DaprConfigStore.impl.ts @@ -14,74 +14,74 @@ const DAPR_API_VERSION = 'v1.0-alpha1' * DaprConfigStore is an adapter which connects to the config store provided by the underlaying Dapr infrastructure */ export class DaprConfigStore extends ConfigStoreBaseClass { - private client: HttpClient - - constructor(config?: StoreBaseConfig) { - super(config?.configStoreName ?? 'DaprConfigStore', { ...config }) - const logger = this.logger - const conf = { - configStoreName: 'configStore', - logger, - ...config, - clientConfig: { - ...getDefaultClientConfig(), - ...config?.clientConfig, - }, - } - - let baseUrl = `${conf.clientConfig.daprHost}:${conf.clientConfig.daprPort}` - if (!baseUrl.startsWith('http://') && !baseUrl.startsWith('https://')) { - baseUrl = `http://${baseUrl}` - } - - const defaultHeaders: Record = { - 'content-type': 'application/json; charset=utf-8', - } - - if (conf.clientConfig.daprApiToken) { - defaultHeaders['dapr-api-token'] = conf.clientConfig.daprApiToken - defaultHeaders['user-agent'] = `purista-dapr-client/v${puristaVersion} http/1` - } - - this.client = new HttpClient({ - logger, - baseUrl, - defaultHeaders, - ...conf.clientConfig, - }) - } - - async getConfigImpl( - ...configNames: ConfigNames - ): Promise> { - const fetchConfigFromStore = async (configName: string) => { - const path = join( - this.config.clientConfig?.daprApiToken ?? DAPR_API_VERSION, - 'configuration', - this.config.configStoreName as string, - ) - - return this.client.get<{ key: string; value: unknown }[]>(path, { query: { key: configName } }) - } - - const result = await Promise.all(configNames.map((configName) => fetchConfigFromStore(configName))) - - const returnValue: Record = {} - - result.forEach((response) => { - response.forEach((entry) => { - returnValue[entry.key] = entry.value - }) - }) - - return returnValue as ObjectWithKeysFromStringArray - } - - async setConfigImpl(_configName: string, _configValue: unknown) { - throw new UnhandledError(StatusCode.NotImplemented, 'setting or changing of configs is not available') - } - - async removeConfigImpl(_configName: string) { - throw new UnhandledError(StatusCode.NotImplemented, 'removing of configs is not available') - } + private client: HttpClient + + constructor(config?: StoreBaseConfig) { + super(config?.configStoreName ?? 'DaprConfigStore', { ...config }) + const logger = this.logger + const conf = { + configStoreName: 'configStore', + logger, + ...config, + clientConfig: { + ...getDefaultClientConfig(), + ...config?.clientConfig, + }, + } + + let baseUrl = `${conf.clientConfig.daprHost}:${conf.clientConfig.daprPort}` + if (!baseUrl.startsWith('http://') && !baseUrl.startsWith('https://')) { + baseUrl = `http://${baseUrl}` + } + + const defaultHeaders: Record = { + 'content-type': 'application/json; charset=utf-8', + } + + if (conf.clientConfig.daprApiToken) { + defaultHeaders['dapr-api-token'] = conf.clientConfig.daprApiToken + defaultHeaders['user-agent'] = `purista-dapr-client/v${puristaVersion} http/1` + } + + this.client = new HttpClient({ + logger, + baseUrl, + defaultHeaders, + ...conf.clientConfig, + }) + } + + async getConfigImpl( + ...configNames: ConfigNames + ): Promise> { + const fetchConfigFromStore = async (configName: string) => { + const path = join( + this.config.clientConfig?.daprApiToken ?? DAPR_API_VERSION, + 'configuration', + this.config.configStoreName as string, + ) + + return this.client.get<{ key: string; value: unknown }[]>(path, { query: { key: configName } }) + } + + const result = await Promise.all(configNames.map(configName => fetchConfigFromStore(configName))) + + const returnValue: Record = {} + + for (const response of result) { + for (const entry of response) { + returnValue[entry.key] = entry.value + } + } + + return returnValue as ObjectWithKeysFromStringArray + } + + async setConfigImpl(_configName: string, _configValue: unknown) { + throw new UnhandledError(StatusCode.NotImplemented, 'setting or changing of configs is not available') + } + + async removeConfigImpl(_configName: string) { + throw new UnhandledError(StatusCode.NotImplemented, 'removing of configs is not available') + } } diff --git a/packages/dapr-sdk/src/DaprConfigStore/daprConfigStore.test.ts b/packages/dapr-sdk/src/DaprConfigStore/daprConfigStore.test.ts index 19333d1a8..09b84b1ae 100644 --- a/packages/dapr-sdk/src/DaprConfigStore/daprConfigStore.test.ts +++ b/packages/dapr-sdk/src/DaprConfigStore/daprConfigStore.test.ts @@ -6,84 +6,84 @@ import { DaprConfigStore } from './DaprConfigStore.impl.js' import type { DaprConfigStoreConfig } from './types/index.js' describe('DaprConfigStore', () => { - let sandbox: SinonSandbox + let sandbox: SinonSandbox - const config: DaprConfigStoreConfig = { - configStoreName: 'test', - clientConfig: { - daprApiVersion: 'v1.0-alpha1', - daprHost: 'localhost', - daprPort: '5000', - daprApiToken: 'token', - }, - } + const config: DaprConfigStoreConfig = { + configStoreName: 'test', + clientConfig: { + daprApiVersion: 'v1.0-alpha1', + daprHost: 'localhost', + daprPort: '5000', + daprApiToken: 'token', + }, + } - beforeEach(() => { - sandbox = createSandbox() - }) + beforeEach(() => { + sandbox = createSandbox() + }) - afterEach(() => { - sandbox.restore() - }) + afterEach(() => { + sandbox.restore() + }) - describe('getConfig', () => { - it('should throw an error if enableGet is false', async () => { - const daprConfigStore = new DaprConfigStore({ - ...config, - enableGet: false, - }) - await expect(daprConfigStore.getConfig('foo')).rejects.toThrow() - }) + describe('getConfig', () => { + it('should throw an error if enableGet is false', async () => { + const daprConfigStore = new DaprConfigStore({ + ...config, + enableGet: false, + }) + await expect(daprConfigStore.getConfig('foo')).rejects.toThrow() + }) - it('should return a key-value pair for each configName requested', async () => { - const httpClientGetStub = sandbox.stub(HttpClient.prototype, 'get') - httpClientGetStub.onFirstCall().resolves([{ key: 'foo', value: 'foo' }]) - httpClientGetStub.onSecondCall().resolves([{ key: 'bar', value: 'bar' }]) + it('should return a key-value pair for each configName requested', async () => { + const httpClientGetStub = sandbox.stub(HttpClient.prototype, 'get') + httpClientGetStub.onFirstCall().resolves([{ key: 'foo', value: 'foo' }]) + httpClientGetStub.onSecondCall().resolves([{ key: 'bar', value: 'bar' }]) - const daprConfigStore = new DaprConfigStore({ - ...config, - enableGet: true, - }) + const daprConfigStore = new DaprConfigStore({ + ...config, + enableGet: true, + }) - const result = await daprConfigStore.getConfig('foo', 'bar') - expect(result).toEqual({ foo: 'foo', bar: 'bar' }) - expect(httpClientGetStub.callCount).toBe(2) - }) - }) + const result = await daprConfigStore.getConfig('foo', 'bar') + expect(result).toEqual({ foo: 'foo', bar: 'bar' }) + expect(httpClientGetStub.callCount).toBe(2) + }) + }) - describe('setConfig', () => { - it('should throw an error if enableSet is false', async () => { - const daprConfigStore = new DaprConfigStore({ - ...config, - enableSet: false, - }) - await expect(daprConfigStore.setConfig('foo', 'bar')).rejects.toThrow() - }) + describe('setConfig', () => { + it('should throw an error if enableSet is false', async () => { + const daprConfigStore = new DaprConfigStore({ + ...config, + enableSet: false, + }) + await expect(daprConfigStore.setConfig('foo', 'bar')).rejects.toThrow() + }) - it('should throw an error as setting configs is not available', async () => { - const daprConfigStore = new DaprConfigStore({ - ...config, - enableSet: true, - }) - await expect(daprConfigStore.setConfig('foo', 'bar')).rejects.toThrow() - }) - }) + it('should throw an error as setting configs is not available', async () => { + const daprConfigStore = new DaprConfigStore({ + ...config, + enableSet: true, + }) + await expect(daprConfigStore.setConfig('foo', 'bar')).rejects.toThrow() + }) + }) - describe('removeConfig', () => { - it('should throw an error if enableRemove is false', async () => { - const daprConfigStore = new DaprConfigStore({ - ...config, - enableRemove: false, - }) - await expect(daprConfigStore.removeConfig('foo')).rejects.toThrow() - }) + describe('removeConfig', () => { + it('should throw an error if enableRemove is false', async () => { + const daprConfigStore = new DaprConfigStore({ + ...config, + enableRemove: false, + }) + await expect(daprConfigStore.removeConfig('foo')).rejects.toThrow() + }) - it('should throw an error as removing configs is not available', async () => { - const daprConfigStore = new DaprConfigStore({ - ...config, - enableRemove: true, - }) - await expect(daprConfigStore.removeConfig('foo')).rejects.toThrow() - }) - }) + it('should throw an error as removing configs is not available', async () => { + const daprConfigStore = new DaprConfigStore({ + ...config, + enableRemove: true, + }) + await expect(daprConfigStore.removeConfig('foo')).rejects.toThrow() + }) + }) }) diff --git a/packages/dapr-sdk/src/DaprConfigStore/types/DaprConfigStoreConfig.ts b/packages/dapr-sdk/src/DaprConfigStore/types/DaprConfigStoreConfig.ts index e0f3270c2..9971bdebc 100644 --- a/packages/dapr-sdk/src/DaprConfigStore/types/DaprConfigStoreConfig.ts +++ b/packages/dapr-sdk/src/DaprConfigStore/types/DaprConfigStoreConfig.ts @@ -4,12 +4,12 @@ import type { DaprClientConfig } from '../../DaprClient/index.js' * Dapr config store configuration */ export type DaprConfigStoreConfig = { - /** - * The name of the config store - */ - configStoreName?: string - /** - * The Dapr client config to interact with Dapr sidecar - */ - clientConfig?: DaprClientConfig + /** + * The name of the config store + */ + configStoreName?: string + /** + * The Dapr client config to interact with Dapr sidecar + */ + clientConfig?: DaprClientConfig } diff --git a/packages/dapr-sdk/src/DaprEventBridge/DaprEventBridge.impl.ts b/packages/dapr-sdk/src/DaprEventBridge/DaprEventBridge.impl.ts index b1ccff5c8..14a0bf0f9 100644 --- a/packages/dapr-sdk/src/DaprEventBridge/DaprEventBridge.impl.ts +++ b/packages/dapr-sdk/src/DaprEventBridge/DaprEventBridge.impl.ts @@ -1,6 +1,6 @@ -import { getDefaultHttpEventBridgeConfig, HttpEventBridge } from '@purista/base-http-bridge' +import { HttpEventBridge, getDefaultHttpEventBridgeConfig } from '@purista/base-http-bridge' import type { CustomMessage, EBMessage, EventBridge, EventBridgeConfig, Subscription } from '@purista/core' -import { EventBridgeEventNames, initLogger, safeBind, StatusCode, UnhandledError } from '@purista/core' +import { EventBridgeEventNames, StatusCode, UnhandledError, initLogger, safeBind } from '@purista/core' import { DaprClient } from '../DaprClient/index.js' import type { DaprPubSubType } from '../types/index.js' @@ -40,76 +40,76 @@ import type { DaprEventBridgeConfig } from './types/index.js' * */ export class DaprEventBridge extends HttpEventBridge implements EventBridge { - private pubSubSubscriptions: DaprPubSubType[] = [] + private pubSubSubscriptions: DaprPubSubType[] = [] - constructor(config: EventBridgeConfig) { - const conf = { - ...getDefaultHttpEventBridgeConfig(), - ...getDefaultConfig(), - ...config, - } + constructor(config: EventBridgeConfig) { + const conf = { + ...getDefaultHttpEventBridgeConfig(), + ...getDefaultConfig(), + ...config, + } - const logger = conf.logger ?? initLogger(config.logLevel, { name: conf.name || 'DaprEventBridge' }) + const logger = conf.logger ?? initLogger(config.logLevel, { name: conf.name || 'DaprEventBridge' }) - const clientConfig = conf.clientConfig + const clientConfig = conf.clientConfig - let baseUrl = `${clientConfig.daprHost}:${clientConfig.daprPort}` - if (!baseUrl.startsWith('http://') && !baseUrl.startsWith('https://')) { - baseUrl = `http://${baseUrl}` - } + let baseUrl = `${clientConfig.daprHost}:${clientConfig.daprPort}` + if (!baseUrl.startsWith('http://') && !baseUrl.startsWith('https://')) { + baseUrl = `http://${baseUrl}` + } - const defaultHeaders: Record = { - 'content-type': 'application/json; charset=utf-8', - } + const defaultHeaders: Record = { + 'content-type': 'application/json; charset=utf-8', + } - if (clientConfig.daprApiToken) { - defaultHeaders['dapr-api-token'] = clientConfig.daprApiToken - defaultHeaders['user-agent'] = `purista-dapr-client/v${puristaVersion} http/1` - } + if (clientConfig.daprApiToken) { + defaultHeaders['dapr-api-token'] = clientConfig.daprApiToken + defaultHeaders['user-agent'] = `purista-dapr-client/v${puristaVersion} http/1` + } - const client = new DaprClient({ - logger, - baseUrl, - defaultHeaders, - ...conf, - }) + const client = new DaprClient({ + logger, + baseUrl, + defaultHeaders, + ...conf, + }) - super(conf, client) - } + super(conf, client) + } - async start() { - this.app.get('/dapr/subscribe', async (c) => { - return c.json(this.pubSubSubscriptions) - }) + async start() { + this.app.get('/dapr/subscribe', async c => { + return c.json(this.pubSubSubscriptions) + }) - /* actors currently not supported/used + /* actors currently not supported/used this.app.delete('/actors/:actorTypeName/:actorId') this.app.put('/actors/:actorTypeName/:actorId/method/:methodName') this.app.put('/actors/:actorTypeName/:actorId/method/timer/:timerName') this.app.put('/actors/:actorTypeName/:actorId/method/remind/:reminderName') */ - this.app.get('/dapr/config', safeBind(configRoute, this)) - - await super.start() - } - - async registerSubscription( - subscription: Subscription, - cb: (message: EBMessage) => Promise | undefined>, - ): Promise { - if (!subscription.eventName) { - const err = new UnhandledError(StatusCode.InternalServerError, 'only subscriptions by event name are supported') - this.emit(EventBridgeEventNames.EventbridgeError, err) - throw err - } - const path = await super.registerSubscription(subscription, cb) - this.pubSubSubscriptions.push({ - pubsubname: this.config.clientConfig?.pubSubName as string, - topic: subscription.eventName, - route: path, - }) - - return path - } + this.app.get('/dapr/config', safeBind(configRoute, this)) + + await super.start() + } + + async registerSubscription( + subscription: Subscription, + cb: (message: EBMessage) => Promise | undefined>, + ): Promise { + if (!subscription.eventName) { + const err = new UnhandledError(StatusCode.InternalServerError, 'only subscriptions by event name are supported') + this.emit(EventBridgeEventNames.EventbridgeError, err) + throw err + } + const path = await super.registerSubscription(subscription, cb) + this.pubSubSubscriptions.push({ + pubsubname: this.config.clientConfig?.pubSubName as string, + topic: subscription.eventName, + route: path, + }) + + return path + } } diff --git a/packages/dapr-sdk/src/DaprEventBridge/getDefaultConfig.impl.ts b/packages/dapr-sdk/src/DaprEventBridge/getDefaultConfig.impl.ts index c789392dd..6ec87429b 100644 --- a/packages/dapr-sdk/src/DaprEventBridge/getDefaultConfig.impl.ts +++ b/packages/dapr-sdk/src/DaprEventBridge/getDefaultConfig.impl.ts @@ -1,17 +1,17 @@ import { getDefaultClientConfig } from '../DaprClient/getDefaultClientConfig.impl.js' export const getDefaultConfig = () => { - const serverPort = process.env.APP_PORT ? parseInt(process.env.APP_PORT, 10) : 8080 + const serverPort = process.env.APP_PORT ? Number.parseInt(process.env.APP_PORT, 10) : 8080 - return { - name: 'DaprEventBridge', - serverHost: process.env.SERVER_HOST ?? '127.0.0.1', - serverPort, - pathPrefix: 'purista', - apiPrefix: 'api', - enableRestApiExpose: true, - subscriptionPayloadAsCloudEvent: true, - commandPayloadAsCloudEvent: false, - clientConfig: getDefaultClientConfig(), - } + return { + name: 'DaprEventBridge', + serverHost: process.env.SERVER_HOST ?? '127.0.0.1', + serverPort, + pathPrefix: 'purista', + apiPrefix: 'api', + enableRestApiExpose: true, + subscriptionPayloadAsCloudEvent: true, + commandPayloadAsCloudEvent: false, + clientConfig: getDefaultClientConfig(), + } } diff --git a/packages/dapr-sdk/src/DaprEventBridge/routes/config.impl.ts b/packages/dapr-sdk/src/DaprEventBridge/routes/config.impl.ts index 08d5627d6..5d5b35c03 100644 --- a/packages/dapr-sdk/src/DaprEventBridge/routes/config.impl.ts +++ b/packages/dapr-sdk/src/DaprEventBridge/routes/config.impl.ts @@ -1,11 +1,11 @@ import type { RouterFunction } from '@purista/base-http-bridge' export const configRoute: RouterFunction = async function (c) { - const payload = { - entities: [], - } + const payload = { + entities: [], + } - this.logger.debug('config requested') + this.logger.debug('config requested') - return c.json(payload) + return c.json(payload) } diff --git a/packages/dapr-sdk/src/DaprEventBridge/routes/config.test.ts b/packages/dapr-sdk/src/DaprEventBridge/routes/config.test.ts index fc0c3f6ad..653c28437 100644 --- a/packages/dapr-sdk/src/DaprEventBridge/routes/config.test.ts +++ b/packages/dapr-sdk/src/DaprEventBridge/routes/config.test.ts @@ -6,33 +6,33 @@ import { createSandbox } from 'sinon' import { configRoute } from './config.impl.js' describe('config route', () => { - let sandbox: SinonSandbox + let sandbox: SinonSandbox - beforeEach(() => { - sandbox = createSandbox() - }) + beforeEach(() => { + sandbox = createSandbox() + }) - afterEach(() => { - sandbox.restore() - sandbox.reset() - }) + afterEach(() => { + sandbox.restore() + sandbox.reset() + }) - it('returns the config object', async () => { - const json = sandbox.stub() - const context = { - json, - } as any as Context + it('returns the config object', async () => { + const json = sandbox.stub() + const context = { + json, + } as any as Context - const bridge = { - logger: getLoggerMock().mock, - } as any + const bridge = { + logger: getLoggerMock().mock, + } as any - const fn = safeBind(configRoute, bridge) - await fn(context) - expect( - json.calledWith({ - entities: [], - }), - ).toBeTruthy() - }) + const fn = safeBind(configRoute, bridge) + await fn(context) + expect( + json.calledWith({ + entities: [], + }), + ).toBeTruthy() + }) }) diff --git a/packages/dapr-sdk/src/DaprEventBridge/types/DaprEventBridgeConfig.ts b/packages/dapr-sdk/src/DaprEventBridge/types/DaprEventBridgeConfig.ts index 494f63dd3..16b7eae4c 100644 --- a/packages/dapr-sdk/src/DaprEventBridge/types/DaprEventBridgeConfig.ts +++ b/packages/dapr-sdk/src/DaprEventBridge/types/DaprEventBridgeConfig.ts @@ -4,7 +4,7 @@ import type { Prettify } from '@purista/core' import type { DaprClientConfig } from '../../DaprClient/index.js' export type DaprEventBridgeConfig = Prettify< - HttpEventBridgeConfig & { - clientConfig?: DaprClientConfig - } + HttpEventBridgeConfig & { + clientConfig?: DaprClientConfig + } > diff --git a/packages/dapr-sdk/src/DaprSecretStore/DaprSecretStore.impl.ts b/packages/dapr-sdk/src/DaprSecretStore/DaprSecretStore.impl.ts index 113db017f..36203e286 100644 --- a/packages/dapr-sdk/src/DaprSecretStore/DaprSecretStore.impl.ts +++ b/packages/dapr-sdk/src/DaprSecretStore/DaprSecretStore.impl.ts @@ -15,82 +15,82 @@ import type { DaprSecretStoreConfig } from './types/index.js' * Dapr currently provides only the possibility to fetch a secret. Creating a new secret, changing an existing secret or removal of secrets is not supported. */ export class DaprSecretStore extends SecretStoreBaseClass { - private client: HttpClient - - constructor(config?: StoreBaseConfig) { - super(config?.secretStoreName ?? 'DaprSecretStore', { ...config }) - const logger = this.logger - const conf = { - secretStoreName: 'secretStore', - logger, - ...config, - clientConfig: { - ...getDefaultClientConfig(), - ...config?.clientConfig, - }, - } - - let baseUrl = `${conf.clientConfig.daprHost}:${conf.clientConfig.daprPort}` - if (!baseUrl.startsWith('http://') && !baseUrl.startsWith('https://')) { - baseUrl = `http://${baseUrl}` - } - - const defaultHeaders: Record = { - 'content-type': 'application/json; charset=utf-8', - } - - if (conf.clientConfig.daprApiToken) { - defaultHeaders['dapr-api-token'] = conf.clientConfig.daprApiToken - defaultHeaders['user-agent'] = `purista-dapr-client/v${puristaVersion} http/1` - } - - this.client = new HttpClient({ - logger, - baseUrl, - defaultHeaders, - ...conf.clientConfig, - }) - } - - protected async getSecretImpl( - ...secretNames: SecretNames - ): Promise> { - const fetchSecretFromStore = async (secretName: string) => { - const path = join( - this.config.clientConfig?.daprApiToken ?? DAPR_API_VERSION, - 'secrets', - this.config.secretStoreName as string, - secretName, - ) - - const query: Record = {} - - if (this.config.metadata?.namespace) { - query['metadata.namespace'] = this.config.metadata?.namespace - } - - return this.client.get>(path, { query }) - } - - const result = await Promise.all(secretNames.map((secretName) => fetchSecretFromStore(secretName))) - - let returnValue: Record = {} - - secretNames.forEach((value, index) => { - returnValue = { - ...result[index], - ...returnValue, - } - }) - - return returnValue as ObjectWithKeysFromStringArray - } - - protected async setSecretImpl(_secretName: string) { - throw new UnhandledError(StatusCode.NotImplemented, 'setting or changing of secrets is not available') - } - - protected async removeSecretImpl(_secretName: string) { - throw new UnhandledError(StatusCode.NotImplemented, 'removing of secrets is not available') - } + private client: HttpClient + + constructor(config?: StoreBaseConfig) { + super(config?.secretStoreName ?? 'DaprSecretStore', { ...config }) + const logger = this.logger + const conf = { + secretStoreName: 'secretStore', + logger, + ...config, + clientConfig: { + ...getDefaultClientConfig(), + ...config?.clientConfig, + }, + } + + let baseUrl = `${conf.clientConfig.daprHost}:${conf.clientConfig.daprPort}` + if (!baseUrl.startsWith('http://') && !baseUrl.startsWith('https://')) { + baseUrl = `http://${baseUrl}` + } + + const defaultHeaders: Record = { + 'content-type': 'application/json; charset=utf-8', + } + + if (conf.clientConfig.daprApiToken) { + defaultHeaders['dapr-api-token'] = conf.clientConfig.daprApiToken + defaultHeaders['user-agent'] = `purista-dapr-client/v${puristaVersion} http/1` + } + + this.client = new HttpClient({ + logger, + baseUrl, + defaultHeaders, + ...conf.clientConfig, + }) + } + + protected async getSecretImpl( + ...secretNames: SecretNames + ): Promise> { + const fetchSecretFromStore = async (secretName: string) => { + const path = join( + this.config.clientConfig?.daprApiToken ?? DAPR_API_VERSION, + 'secrets', + this.config.secretStoreName as string, + secretName, + ) + + const query: Record = {} + + if (this.config.metadata?.namespace) { + query['metadata.namespace'] = this.config.metadata?.namespace + } + + return this.client.get>(path, { query }) + } + + const result = await Promise.all(secretNames.map(secretName => fetchSecretFromStore(secretName))) + + let returnValue: Record = {} + + secretNames.forEach((value, index) => { + returnValue = { + ...result[index], + ...returnValue, + } + }) + + return returnValue as ObjectWithKeysFromStringArray + } + + protected async setSecretImpl(_secretName: string) { + throw new UnhandledError(StatusCode.NotImplemented, 'setting or changing of secrets is not available') + } + + protected async removeSecretImpl(_secretName: string) { + throw new UnhandledError(StatusCode.NotImplemented, 'removing of secrets is not available') + } } diff --git a/packages/dapr-sdk/src/DaprSecretStore/daprSecretStore.test.ts b/packages/dapr-sdk/src/DaprSecretStore/daprSecretStore.test.ts index 3189c1f63..1c37c7fb8 100644 --- a/packages/dapr-sdk/src/DaprSecretStore/daprSecretStore.test.ts +++ b/packages/dapr-sdk/src/DaprSecretStore/daprSecretStore.test.ts @@ -6,146 +6,146 @@ import { DAPR_API_VERSION } from '../types/index.js' import { DaprSecretStore } from './DaprSecretStore.impl.js' describe('DaprSecretStore', () => { - let sandbox: SinonSandbox - - const config = { - storeName: 'mySecretStore', - enableGet: true, - secretStoreName: 'test', - clientConfig: { - daprHost: 'localhost', - daprPort: '5000', - daprApiToken: 'myToken', - daprApiVersion: DAPR_API_VERSION, - }, - } - - beforeEach(() => { - sandbox = createSandbox() - }) - - afterEach(() => { - sandbox.restore() - sandbox.reset() - }) - - describe('getSecret', () => { - it('should fetch secrets from the Dapr secret store', async () => { - const secretName1 = 'mySecret1' - const secretName2 = 'mySecret2' - const secretValue1 = 'mySecretValue1' - const secretValue2 = 'mySecretValue2' - const httpClientGetStub = sandbox.stub(HttpClient.prototype, 'get') - httpClientGetStub.onFirstCall().resolves({ [secretName1]: secretValue1 }) - httpClientGetStub.onSecondCall().resolves({ [secretName2]: secretValue2 }) - - const secretStore = new DaprSecretStore(config) - const result = await secretStore.getSecret(secretName1, secretName2) - - expect(result).toEqual({ - [secretName1]: secretValue1, - [secretName2]: secretValue2, - }) - expect(httpClientGetStub.callCount).toBe(2) - }) - - it('should throw an error if get secret is disabled by config', async () => { - const disabledConfig = { - storeName: 'mySecretStore', - enableGet: false, - config: { - secretStoreName: 'test', - clientConfig: { - daprHost: 'localhost', - daprPort: '5000', - daprApiToken: 'myToken', - daprApiVersion: DAPR_API_VERSION, - }, - }, - } - const disabledSecretStore = new DaprSecretStore(disabledConfig) - - await expect(disabledSecretStore.getSecret('mySecret')).rejects.toThrow( - 'get secret from store is disabled by config', - ) - }) - }) - - describe('setSecret', () => { - it('should throw an UnhandledError with StatusCode.NotImplemented', async () => { - const secretName = 'test-secret' - - const secretStore = new DaprSecretStore({ - ...config, - enableGet: true, - enableSet: true, - enableRemove: true, - }) - - try { - await secretStore.setSecret(secretName, 'test') - } catch (err) { - expect(err).toBeInstanceOf(UnhandledError) - expect((err as UnhandledError).errorCode).toBe(501) - expect((err as UnhandledError).message).toBe('setting or changing of secrets is not available') - } - }) - - it('should throw an UnhandledError with StatusCode.Unauthorized if enableSet is false', async () => { - const secretName = 'test-secret' - const secretStore = new DaprSecretStore({ - ...config, - enableGet: true, - enableSet: false, - enableRemove: true, - }) - - try { - await secretStore.setSecret(secretName, 'test') - } catch (err) { - expect(err).toBeInstanceOf(UnhandledError) - expect((err as UnhandledError).errorCode).toBe(401) - expect((err as UnhandledError).message).toBe('set secret at store is disabled by config') - } - }) - }) - - describe('removeSecret', () => { - it('should throw an UnhandledError with StatusCode.NotImplemented', async () => { - const secretName = 'test-secret' - - const secretStore = new DaprSecretStore({ - ...config, - enableGet: true, - enableSet: true, - enableRemove: true, - }) - - try { - await secretStore.removeSecret(secretName) - } catch (err) { - expect(err).toBeInstanceOf(UnhandledError) - expect((err as UnhandledError).errorCode).toBe(501) - expect((err as UnhandledError).message).toBe('removing of secrets is not available') - } - }) - - it('should throw an UnhandledError with StatusCode.Unauthorized if enableSet is false', async () => { - const secretName = 'test-secret' - const secretStore = new DaprSecretStore({ - ...config, - enableGet: false, - enableSet: false, - enableRemove: false, - }) - - try { - await secretStore.removeSecret(secretName) - } catch (err) { - expect(err).toBeInstanceOf(UnhandledError) - expect((err as UnhandledError).errorCode).toBe(401) - expect((err as UnhandledError).message).toBe('remove secret from store is disabled by config') - } - }) - }) + let sandbox: SinonSandbox + + const config = { + storeName: 'mySecretStore', + enableGet: true, + secretStoreName: 'test', + clientConfig: { + daprHost: 'localhost', + daprPort: '5000', + daprApiToken: 'myToken', + daprApiVersion: DAPR_API_VERSION, + }, + } + + beforeEach(() => { + sandbox = createSandbox() + }) + + afterEach(() => { + sandbox.restore() + sandbox.reset() + }) + + describe('getSecret', () => { + it('should fetch secrets from the Dapr secret store', async () => { + const secretName1 = 'mySecret1' + const secretName2 = 'mySecret2' + const secretValue1 = 'mySecretValue1' + const secretValue2 = 'mySecretValue2' + const httpClientGetStub = sandbox.stub(HttpClient.prototype, 'get') + httpClientGetStub.onFirstCall().resolves({ [secretName1]: secretValue1 }) + httpClientGetStub.onSecondCall().resolves({ [secretName2]: secretValue2 }) + + const secretStore = new DaprSecretStore(config) + const result = await secretStore.getSecret(secretName1, secretName2) + + expect(result).toEqual({ + [secretName1]: secretValue1, + [secretName2]: secretValue2, + }) + expect(httpClientGetStub.callCount).toBe(2) + }) + + it('should throw an error if get secret is disabled by config', async () => { + const disabledConfig = { + storeName: 'mySecretStore', + enableGet: false, + config: { + secretStoreName: 'test', + clientConfig: { + daprHost: 'localhost', + daprPort: '5000', + daprApiToken: 'myToken', + daprApiVersion: DAPR_API_VERSION, + }, + }, + } + const disabledSecretStore = new DaprSecretStore(disabledConfig) + + await expect(disabledSecretStore.getSecret('mySecret')).rejects.toThrow( + 'get secret from store is disabled by config', + ) + }) + }) + + describe('setSecret', () => { + it('should throw an UnhandledError with StatusCode.NotImplemented', async () => { + const secretName = 'test-secret' + + const secretStore = new DaprSecretStore({ + ...config, + enableGet: true, + enableSet: true, + enableRemove: true, + }) + + try { + await secretStore.setSecret(secretName, 'test') + } catch (err) { + expect(err).toBeInstanceOf(UnhandledError) + expect((err as UnhandledError).errorCode).toBe(501) + expect((err as UnhandledError).message).toBe('setting or changing of secrets is not available') + } + }) + + it('should throw an UnhandledError with StatusCode.Unauthorized if enableSet is false', async () => { + const secretName = 'test-secret' + const secretStore = new DaprSecretStore({ + ...config, + enableGet: true, + enableSet: false, + enableRemove: true, + }) + + try { + await secretStore.setSecret(secretName, 'test') + } catch (err) { + expect(err).toBeInstanceOf(UnhandledError) + expect((err as UnhandledError).errorCode).toBe(401) + expect((err as UnhandledError).message).toBe('set secret at store is disabled by config') + } + }) + }) + + describe('removeSecret', () => { + it('should throw an UnhandledError with StatusCode.NotImplemented', async () => { + const secretName = 'test-secret' + + const secretStore = new DaprSecretStore({ + ...config, + enableGet: true, + enableSet: true, + enableRemove: true, + }) + + try { + await secretStore.removeSecret(secretName) + } catch (err) { + expect(err).toBeInstanceOf(UnhandledError) + expect((err as UnhandledError).errorCode).toBe(501) + expect((err as UnhandledError).message).toBe('removing of secrets is not available') + } + }) + + it('should throw an UnhandledError with StatusCode.Unauthorized if enableSet is false', async () => { + const secretName = 'test-secret' + const secretStore = new DaprSecretStore({ + ...config, + enableGet: false, + enableSet: false, + enableRemove: false, + }) + + try { + await secretStore.removeSecret(secretName) + } catch (err) { + expect(err).toBeInstanceOf(UnhandledError) + expect((err as UnhandledError).errorCode).toBe(401) + expect((err as UnhandledError).message).toBe('remove secret from store is disabled by config') + } + }) + }) }) diff --git a/packages/dapr-sdk/src/DaprSecretStore/types/DaprSecretStoreConfig.ts b/packages/dapr-sdk/src/DaprSecretStore/types/DaprSecretStoreConfig.ts index dbe093ddc..b71be8915 100644 --- a/packages/dapr-sdk/src/DaprSecretStore/types/DaprSecretStoreConfig.ts +++ b/packages/dapr-sdk/src/DaprSecretStore/types/DaprSecretStoreConfig.ts @@ -4,23 +4,23 @@ import type { DaprClientConfig } from '../../DaprClient/index.js' * Dapr secret store configuration */ export type DaprSecretStoreConfig = { - /** - * The name of the secret store - */ - secretStoreName?: string + /** + * The name of the secret store + */ + secretStoreName?: string - /** - * The Dapr client config to interact with Dapr sidecar - */ - clientConfig?: DaprClientConfig + /** + * The Dapr client config to interact with Dapr sidecar + */ + clientConfig?: DaprClientConfig - /** - * Dapr secret store metadata - */ - metadata?: { - /** - * In case of deploying into namespace other than default, the namespace (e.g. production) must be set - */ - namespace?: string - } + /** + * Dapr secret store metadata + */ + metadata?: { + /** + * In case of deploying into namespace other than default, the namespace (e.g. production) must be set + */ + namespace?: string + } } diff --git a/packages/dapr-sdk/src/DaprStateStore/DaprStateStore.impl.ts b/packages/dapr-sdk/src/DaprStateStore/DaprStateStore.impl.ts index d9c095b3d..c35a15509 100644 --- a/packages/dapr-sdk/src/DaprStateStore/DaprStateStore.impl.ts +++ b/packages/dapr-sdk/src/DaprStateStore/DaprStateStore.impl.ts @@ -13,97 +13,97 @@ import type { DaprStateStoreConfig } from './types/index.js' * DaprStateStore is an adapter which connects to the state store provided by the underlaying Dapr infrastructure */ export class DaprStateStore extends StateStoreBaseClass { - private client: HttpClient - - constructor(config?: StoreBaseConfig) { - super(config?.stateStoreName ?? 'DaprStateStore', { ...config }) - const logger = this.logger - const conf = { - stateStoreName: 'stateStore', - logger, - ...config, - clientConfig: { - ...getDefaultClientConfig(), - ...config?.clientConfig, - }, - } - - let baseUrl = `${conf.clientConfig.daprHost}:${conf.clientConfig.daprPort}` - if (!baseUrl.startsWith('http://') && !baseUrl.startsWith('https://')) { - baseUrl = `http://${baseUrl}` - } - - const defaultHeaders: Record = { - 'content-type': 'application/json; charset=utf-8', - } - - if (conf.clientConfig.daprApiToken) { - defaultHeaders['dapr-api-token'] = conf.clientConfig.daprApiToken - defaultHeaders['user-agent'] = `purista-dapr-client/v${puristaVersion} http/1` - } - - this.client = new HttpClient({ - logger, - baseUrl, - defaultHeaders, - ...conf.clientConfig, - }) - } - - protected async getStateImpl( - ...stateNames: StateNames - ): Promise> { - const fetchStatesFromStore = async (stateName: string) => { - const path = join( - this.config.clientConfig?.daprApiToken ?? DAPR_API_VERSION, - 'state', - this.config.stateStoreName as string, - stateName, - ) - - const query: Record = { - 'metadata.contentType': 'application/json', - } - - return this.client.get(path, { query }) - } - - const result = await Promise.all(stateNames.map((stateName) => fetchStatesFromStore(stateName))) - - const returnValue: Record = {} - - stateNames.forEach((value, index) => { - returnValue[value] = result[index] - }) - - return returnValue as ObjectWithKeysFromStringArray - } - - protected async setStateImpl(stateName: string, stateValue: unknown) { - const path = join( - this.config.clientConfig?.daprApiToken ?? DAPR_API_VERSION, - 'state', - this.config.stateStoreName as string, - ) - - const payload = [ - { - key: stateName, - value: stateValue, - }, - ] - - await this.client.post(path, payload) - } - - protected async removeStateImpl(stateName: string) { - const path = join( - this.config.clientConfig?.daprApiToken ?? DAPR_API_VERSION, - 'state', - this.config.stateStoreName as string, - stateName, - ) - - await this.client.delete(path) - } + private client: HttpClient + + constructor(config?: StoreBaseConfig) { + super(config?.stateStoreName ?? 'DaprStateStore', { ...config }) + const logger = this.logger + const conf = { + stateStoreName: 'stateStore', + logger, + ...config, + clientConfig: { + ...getDefaultClientConfig(), + ...config?.clientConfig, + }, + } + + let baseUrl = `${conf.clientConfig.daprHost}:${conf.clientConfig.daprPort}` + if (!baseUrl.startsWith('http://') && !baseUrl.startsWith('https://')) { + baseUrl = `http://${baseUrl}` + } + + const defaultHeaders: Record = { + 'content-type': 'application/json; charset=utf-8', + } + + if (conf.clientConfig.daprApiToken) { + defaultHeaders['dapr-api-token'] = conf.clientConfig.daprApiToken + defaultHeaders['user-agent'] = `purista-dapr-client/v${puristaVersion} http/1` + } + + this.client = new HttpClient({ + logger, + baseUrl, + defaultHeaders, + ...conf.clientConfig, + }) + } + + protected async getStateImpl( + ...stateNames: StateNames + ): Promise> { + const fetchStatesFromStore = async (stateName: string) => { + const path = join( + this.config.clientConfig?.daprApiToken ?? DAPR_API_VERSION, + 'state', + this.config.stateStoreName as string, + stateName, + ) + + const query: Record = { + 'metadata.contentType': 'application/json', + } + + return this.client.get(path, { query }) + } + + const result = await Promise.all(stateNames.map(stateName => fetchStatesFromStore(stateName))) + + const returnValue: Record = {} + + stateNames.forEach((value, index) => { + returnValue[value] = result[index] + }) + + return returnValue as ObjectWithKeysFromStringArray + } + + protected async setStateImpl(stateName: string, stateValue: unknown) { + const path = join( + this.config.clientConfig?.daprApiToken ?? DAPR_API_VERSION, + 'state', + this.config.stateStoreName as string, + ) + + const payload = [ + { + key: stateName, + value: stateValue, + }, + ] + + await this.client.post(path, payload) + } + + protected async removeStateImpl(stateName: string) { + const path = join( + this.config.clientConfig?.daprApiToken ?? DAPR_API_VERSION, + 'state', + this.config.stateStoreName as string, + stateName, + ) + + await this.client.delete(path) + } } diff --git a/packages/dapr-sdk/src/DaprStateStore/daprStateStore.test.ts b/packages/dapr-sdk/src/DaprStateStore/daprStateStore.test.ts index 8ff52b11c..6b0ff508f 100644 --- a/packages/dapr-sdk/src/DaprStateStore/daprStateStore.test.ts +++ b/packages/dapr-sdk/src/DaprStateStore/daprStateStore.test.ts @@ -6,139 +6,139 @@ import { DAPR_API_VERSION } from '../types/index.js' import { DaprStateStore } from './DaprStateStore.impl.js' describe('DaprStateStore', () => { - let sandbox: SinonSandbox - - const config = { - storeName: 'myStateStore', - enableGet: true, - stateStoreName: 'test', - clientConfig: { - daprHost: 'localhost', - daprPort: '5000', - daprApiToken: 'myToken', - daprApiVersion: DAPR_API_VERSION, - }, - } - - beforeEach(() => { - sandbox = createSandbox() - }) - - afterEach(() => { - sandbox.restore() - sandbox.reset() - }) - - describe('getState', () => { - it('should fetch states from the Dapr state store', async () => { - const stateName1 = 'myState1' - const stateName2 = 'myState2' - const stateValue1 = 'myStateValue1' - const stateValue2 = 'myStateValue2' - const httpClientGetStub = sandbox.stub(HttpClient.prototype, 'get') - httpClientGetStub.onFirstCall().resolves(stateValue1) - httpClientGetStub.onSecondCall().resolves(stateValue2) - - const stateStore = new DaprStateStore(config) - const result = await stateStore.getState(stateName1, stateName2) - - expect(result).toStrictEqual({ - [stateName1]: stateValue1, - [stateName2]: stateValue2, - }) - - expect(httpClientGetStub.callCount).toBe(2) - }) - - it('should throw an error if get state is disabled by config', async () => { - const disabledConfig = { - storeName: 'myStateStore', - enableGet: false, - config: { - stateStoreName: 'test', - clientConfig: { - daprHost: 'localhost', - daprPort: '5000', - daprApiToken: 'myToken', - daprApiVersion: DAPR_API_VERSION, - }, - }, - } - const disabledstateStore = new DaprStateStore(disabledConfig) - - await expect(disabledstateStore.getState('myState')).rejects.toThrow('get state from store is disabled by config') - }) - }) - - describe('setState', () => { - it('should set the status', async () => { - const stateName = 'test-state' - - const httpClientGetStub = sandbox.stub(HttpClient.prototype, 'post') - httpClientGetStub.onFirstCall().resolves() - - const stateStore = new DaprStateStore({ - ...config, - enableGet: true, - enableSet: true, - enableRemove: true, - }) - - await expect(stateStore.setState(stateName, {})).resolves.toBeUndefined() - }) - - it('should throw an UnhandledError with StatusCode.Unauthorized if enableSet is false', async () => { - const stateName = 'test-state' - const stateStore = new DaprStateStore({ - ...config, - enableGet: true, - enableSet: false, - enableRemove: true, - }) - - try { - await stateStore.setState(stateName, {}) - } catch (err) { - expect(err).toBeInstanceOf(UnhandledError) - expect((err as UnhandledError).errorCode).toBe(401) - expect((err as UnhandledError).message).toBe('set state at store is disabled by config') - } - }) - }) - - describe('removeState', () => { - it('should remove the status', async () => { - const stateName = 'test-state' - - const httpClientGetStub = sandbox.stub(HttpClient.prototype, 'delete') - httpClientGetStub.onFirstCall().resolves() - - const stateStore = new DaprStateStore({ - ...config, - enableGet: true, - enableSet: true, - enableRemove: true, - }) - - await expect(stateStore.removeState(stateName)).resolves.toBeUndefined() - }) - - it('should throw an UnhandledError with StatusCode.Unauthorized if enableSet is false', async () => { - const stateName = 'test-state' - const stateStore = new DaprStateStore({ - ...config, - enableGet: false, - enableSet: false, - enableRemove: false, - }) - - try { - await stateStore.removeState(stateName) - } catch (err) { - expect(err).toBeInstanceOf(UnhandledError) - expect((err as UnhandledError).errorCode).toBe(401) - expect((err as UnhandledError).message).toBe('remove state from store is disabled by config') - } - }) - }) + let sandbox: SinonSandbox + + const config = { + storeName: 'myStateStore', + enableGet: true, + stateStoreName: 'test', + clientConfig: { + daprHost: 'localhost', + daprPort: '5000', + daprApiToken: 'myToken', + daprApiVersion: DAPR_API_VERSION, + }, + } + + beforeEach(() => { + sandbox = createSandbox() + }) + + afterEach(() => { + sandbox.restore() + sandbox.reset() + }) + + describe('getState', () => { + it('should fetch states from the Dapr state store', async () => { + const stateName1 = 'myState1' + const stateName2 = 'myState2' + const stateValue1 = 'myStateValue1' + const stateValue2 = 'myStateValue2' + const httpClientGetStub = sandbox.stub(HttpClient.prototype, 'get') + httpClientGetStub.onFirstCall().resolves(stateValue1) + httpClientGetStub.onSecondCall().resolves(stateValue2) + + const stateStore = new DaprStateStore(config) + const result = await stateStore.getState(stateName1, stateName2) + + expect(result).toStrictEqual({ + [stateName1]: stateValue1, + [stateName2]: stateValue2, + }) + + expect(httpClientGetStub.callCount).toBe(2) + }) + + it('should throw an error if get state is disabled by config', async () => { + const disabledConfig = { + storeName: 'myStateStore', + enableGet: false, + config: { + stateStoreName: 'test', + clientConfig: { + daprHost: 'localhost', + daprPort: '5000', + daprApiToken: 'myToken', + daprApiVersion: DAPR_API_VERSION, + }, + }, + } + const disabledstateStore = new DaprStateStore(disabledConfig) + + await expect(disabledstateStore.getState('myState')).rejects.toThrow('get state from store is disabled by config') + }) + }) + + describe('setState', () => { + it('should set the status', async () => { + const stateName = 'test-state' + + const httpClientGetStub = sandbox.stub(HttpClient.prototype, 'post') + httpClientGetStub.onFirstCall().resolves() + + const stateStore = new DaprStateStore({ + ...config, + enableGet: true, + enableSet: true, + enableRemove: true, + }) + + await expect(stateStore.setState(stateName, {})).resolves.toBeUndefined() + }) + + it('should throw an UnhandledError with StatusCode.Unauthorized if enableSet is false', async () => { + const stateName = 'test-state' + const stateStore = new DaprStateStore({ + ...config, + enableGet: true, + enableSet: false, + enableRemove: true, + }) + + try { + await stateStore.setState(stateName, {}) + } catch (err) { + expect(err).toBeInstanceOf(UnhandledError) + expect((err as UnhandledError).errorCode).toBe(401) + expect((err as UnhandledError).message).toBe('set state at store is disabled by config') + } + }) + }) + + describe('removeState', () => { + it('should remove the status', async () => { + const stateName = 'test-state' + + const httpClientGetStub = sandbox.stub(HttpClient.prototype, 'delete') + httpClientGetStub.onFirstCall().resolves() + + const stateStore = new DaprStateStore({ + ...config, + enableGet: true, + enableSet: true, + enableRemove: true, + }) + + await expect(stateStore.removeState(stateName)).resolves.toBeUndefined() + }) + + it('should throw an UnhandledError with StatusCode.Unauthorized if enableSet is false', async () => { + const stateName = 'test-state' + const stateStore = new DaprStateStore({ + ...config, + enableGet: false, + enableSet: false, + enableRemove: false, + }) + + try { + await stateStore.removeState(stateName) + } catch (err) { + expect(err).toBeInstanceOf(UnhandledError) + expect((err as UnhandledError).errorCode).toBe(401) + expect((err as UnhandledError).message).toBe('remove state from store is disabled by config') + } + }) + }) }) diff --git a/packages/dapr-sdk/src/DaprStateStore/types/DaprStateStoreConfig.ts b/packages/dapr-sdk/src/DaprStateStore/types/DaprStateStoreConfig.ts index 89c94b8df..500175322 100644 --- a/packages/dapr-sdk/src/DaprStateStore/types/DaprStateStoreConfig.ts +++ b/packages/dapr-sdk/src/DaprStateStore/types/DaprStateStoreConfig.ts @@ -4,13 +4,13 @@ import type { DaprClientConfig } from '../../DaprClient/index.js' * Dapr state store configuration */ export type DaprStateStoreConfig = { - /** - * The name of the state store - */ - stateStoreName?: string + /** + * The name of the state store + */ + stateStoreName?: string - /** - * The Dapr client config to interact with Dapr sidecar - */ - clientConfig?: DaprClientConfig + /** + * The Dapr client config to interact with Dapr sidecar + */ + clientConfig?: DaprClientConfig } diff --git a/packages/dapr-sdk/src/index.test.ts b/packages/dapr-sdk/src/index.test.ts index ba6f1bc86..4d77b3c96 100644 --- a/packages/dapr-sdk/src/index.test.ts +++ b/packages/dapr-sdk/src/index.test.ts @@ -1,32 +1,32 @@ import { - DaprClient, - DaprConfigStore, - DaprEventBridge, - DaprSecretStore, - DaprStateStore, - puristaVersion, + DaprClient, + DaprConfigStore, + DaprEventBridge, + DaprSecretStore, + DaprStateStore, + puristaVersion, } from './index.js' describe('Export', () => { - it('has a version', () => { - expect(puristaVersion).toBeDefined() - }) - it('exports the DaprClient', () => { - expect(DaprClient).toBeDefined() - }) - it('exports the DaprConfigStore', () => { - expect(DaprConfigStore).toBeDefined() - }) + it('has a version', () => { + expect(puristaVersion).toBeDefined() + }) + it('exports the DaprClient', () => { + expect(DaprClient).toBeDefined() + }) + it('exports the DaprConfigStore', () => { + expect(DaprConfigStore).toBeDefined() + }) - it('exports the DaprEventBridge', () => { - expect(DaprEventBridge).toBeDefined() - }) + it('exports the DaprEventBridge', () => { + expect(DaprEventBridge).toBeDefined() + }) - it('exports the DaprSecretStore', () => { - expect(DaprSecretStore).toBeDefined() - }) + it('exports the DaprSecretStore', () => { + expect(DaprSecretStore).toBeDefined() + }) - it('exports the DaprStateStore', () => { - expect(DaprStateStore).toBeDefined() - }) + it('exports the DaprStateStore', () => { + expect(DaprStateStore).toBeDefined() + }) }) diff --git a/packages/dapr-sdk/src/test/integation.test.ts b/packages/dapr-sdk/src/test/integation.test.ts index 288b82af0..64d6878d1 100644 --- a/packages/dapr-sdk/src/test/integation.test.ts +++ b/packages/dapr-sdk/src/test/integation.test.ts @@ -5,26 +5,26 @@ import { createSandbox } from 'sinon' import { DaprEventBridge } from '../DaprEventBridge/index.js' describe('DaprEventbridge', () => { - let bridge: DaprEventBridge - const sandbox = createSandbox() - const logger = getLoggerMock(sandbox) + let bridge: DaprEventBridge + const sandbox = createSandbox() + const logger = getLoggerMock(sandbox) - beforeAll(async () => { - bridge = new DaprEventBridge({ - logger: logger.mock, - serve, - }) - }) + beforeAll(async () => { + bridge = new DaprEventBridge({ + logger: logger.mock, + serve, + }) + }) - afterAll(async () => { - await bridge.destroy() - }) + afterAll(async () => { + await bridge.destroy() + }) - afterEach(() => { - sandbox.reset() - }) + afterEach(() => { + sandbox.reset() + }) - it('works', async () => { - await bridge.start() - }) + it('works', async () => { + await bridge.start() + }) }) diff --git a/packages/dapr-sdk/src/types/pubsub/BulkSubscribeConfig.type.ts b/packages/dapr-sdk/src/types/pubsub/BulkSubscribeConfig.type.ts index ead267616..92fefdd77 100644 --- a/packages/dapr-sdk/src/types/pubsub/BulkSubscribeConfig.type.ts +++ b/packages/dapr-sdk/src/types/pubsub/BulkSubscribeConfig.type.ts @@ -17,12 +17,12 @@ limitations under the License. * BulkSubscribeConfig defines the configuration for a bulk subscription **/ export type BulkSubscribeConfig = { - // Flag to enable/disable bulk subscribe - enabled: boolean + // Flag to enable/disable bulk subscribe + enabled: boolean - // Max number of messages to be sent in a single bulk request - maxMessagesCount?: number + // Max number of messages to be sent in a single bulk request + maxMessagesCount?: number - // Max duration to wait for messages to be sent in a single bulk request - maxAwaitDurationMs?: number + // Max duration to wait for messages to be sent in a single bulk request + maxAwaitDurationMs?: number } diff --git a/packages/dapr-sdk/src/types/pubsub/BulkSubscribeOptions.type.ts b/packages/dapr-sdk/src/types/pubsub/BulkSubscribeOptions.type.ts index 84ec9b960..15d816c39 100644 --- a/packages/dapr-sdk/src/types/pubsub/BulkSubscribeOptions.type.ts +++ b/packages/dapr-sdk/src/types/pubsub/BulkSubscribeOptions.type.ts @@ -18,15 +18,15 @@ import type { KeyValueType } from './KeyValue.type.js' * BulkSubscribeOptions enlists the options for bulk subscribe **/ export type BulkSubscribeOptions = { - // The route creation for a single route or DaprPubSubRouteType - route?: string | DaprPubSubRouteType + // The route creation for a single route or DaprPubSubRouteType + route?: string | DaprPubSubRouteType - // Metadata for the subscription - metadata?: KeyValueType + // Metadata for the subscription + metadata?: KeyValueType - // Max number of messages to be sent in a single bulk request - maxMessagesCount?: number + // Max number of messages to be sent in a single bulk request + maxMessagesCount?: number - // Max duration to wait for messages to be sent in a single bulk request - maxAwaitDurationMs?: number + // Max duration to wait for messages to be sent in a single bulk request + maxAwaitDurationMs?: number } diff --git a/packages/dapr-sdk/src/types/pubsub/BulkSubscribeResponse.type.ts b/packages/dapr-sdk/src/types/pubsub/BulkSubscribeResponse.type.ts index db6d536fc..3ce674509 100644 --- a/packages/dapr-sdk/src/types/pubsub/BulkSubscribeResponse.type.ts +++ b/packages/dapr-sdk/src/types/pubsub/BulkSubscribeResponse.type.ts @@ -17,6 +17,6 @@ import type { BulkSubscribeResponseEntry } from './BulkSubscribeResponseEntry.ty * BulkSubscribeResponse is the response for a bulk subscribe request **/ export type BulkSubscribeResponse = { - // An array of bulk subscribe response entries - statuses: BulkSubscribeResponseEntry[] + // An array of bulk subscribe response entries + statuses: BulkSubscribeResponseEntry[] } diff --git a/packages/dapr-sdk/src/types/pubsub/BulkSubscribeResponseEntry.type.ts b/packages/dapr-sdk/src/types/pubsub/BulkSubscribeResponseEntry.type.ts index 58a296b34..d7e35617d 100644 --- a/packages/dapr-sdk/src/types/pubsub/BulkSubscribeResponseEntry.type.ts +++ b/packages/dapr-sdk/src/types/pubsub/BulkSubscribeResponseEntry.type.ts @@ -17,9 +17,9 @@ import type DaprPubSubStatusEnum from './DaprPubSubStatus.enum.js' * BulkSubscribeResponseEntry is the response entry for a bulk subscribe request **/ export type BulkSubscribeResponseEntry = { - // The id of the bulk subscribe entry - entryId: string + // The id of the bulk subscribe entry + entryId: string - // The response status of the bulk subscribe entry - status: DaprPubSubStatusEnum + // The response status of the bulk subscribe entry + status: DaprPubSubStatusEnum } diff --git a/packages/dapr-sdk/src/types/pubsub/DaprPubSub.type.ts b/packages/dapr-sdk/src/types/pubsub/DaprPubSub.type.ts index 1b49810a3..e791c8872 100644 --- a/packages/dapr-sdk/src/types/pubsub/DaprPubSub.type.ts +++ b/packages/dapr-sdk/src/types/pubsub/DaprPubSub.type.ts @@ -20,24 +20,24 @@ import type { KeyValueType } from './KeyValue.type.js' * DaprPubSubType is the Type used by the Dapr API to interface with its PubSub component */ export type DaprPubSubType = { - // The pubsub component name - pubsubname: string + // The pubsub component name + pubsubname: string - // The topic name - topic: string + // The topic name + topic: string - // Metadata - metadata?: KeyValueType + // Metadata + metadata?: KeyValueType - // A singular route to send the event to - route?: string + // A singular route to send the event to + route?: string - // A rule based route to send the event to - routes?: DaprPubSubRouteType + // A rule based route to send the event to + routes?: DaprPubSubRouteType - // The path to send unprocessable events to - deadLetterTopic?: string + // The path to send unprocessable events to + deadLetterTopic?: string - // The settings for bulk subscribe - bulkSubscribe?: BulkSubscribeConfig + // The settings for bulk subscribe + bulkSubscribe?: BulkSubscribeConfig } diff --git a/packages/dapr-sdk/src/types/pubsub/DaprPubSubCallback.type.ts b/packages/dapr-sdk/src/types/pubsub/DaprPubSubCallback.type.ts index af4d56bf7..a819fa596 100644 --- a/packages/dapr-sdk/src/types/pubsub/DaprPubSubCallback.type.ts +++ b/packages/dapr-sdk/src/types/pubsub/DaprPubSubCallback.type.ts @@ -11,4 +11,4 @@ See the License for the specific language governing permissions and limitations under the License. */ -export type TypeDaprPubSubCallback = (data: any, headers: object) => Promise +export type TypeDaprPubSubCallback = (data: any, headers: object) => Promise diff --git a/packages/dapr-sdk/src/types/pubsub/DaprPubSubRouteType.type.ts b/packages/dapr-sdk/src/types/pubsub/DaprPubSubRouteType.type.ts index c07598c11..5e03cdd86 100644 --- a/packages/dapr-sdk/src/types/pubsub/DaprPubSubRouteType.type.ts +++ b/packages/dapr-sdk/src/types/pubsub/DaprPubSubRouteType.type.ts @@ -18,9 +18,9 @@ import type { DaprPubSubRuleType } from './DaprPubSubRuleType.type.js' * DaprPubSubRouteType Defines the rules for a route */ export type DaprPubSubRouteType = { - // The rule - rules?: DaprPubSubRuleType[] + // The rule + rules?: DaprPubSubRuleType[] - // The default path to use if no rule was matched - default?: string + // The default path to use if no rule was matched + default?: string } diff --git a/packages/dapr-sdk/src/types/pubsub/DaprPubSubRuleType.type.ts b/packages/dapr-sdk/src/types/pubsub/DaprPubSubRuleType.type.ts index 57887adbe..09834d538 100644 --- a/packages/dapr-sdk/src/types/pubsub/DaprPubSubRuleType.type.ts +++ b/packages/dapr-sdk/src/types/pubsub/DaprPubSubRuleType.type.ts @@ -17,9 +17,9 @@ limitations under the License. * DaprPubSubRuleType defines a rule set */ export type DaprPubSubRuleType = { - // Match rule (e.g. event.type == "hello") - match: string + // Match rule (e.g. event.type == "hello") + match: string - // The path to send the event towards if it matches - path: string + // The path to send the event towards if it matches + path: string } diff --git a/packages/dapr-sdk/src/types/pubsub/DaprPubSubStatus.enum.ts b/packages/dapr-sdk/src/types/pubsub/DaprPubSubStatus.enum.ts index fe5d481ab..0dd00e156 100644 --- a/packages/dapr-sdk/src/types/pubsub/DaprPubSubStatus.enum.ts +++ b/packages/dapr-sdk/src/types/pubsub/DaprPubSubStatus.enum.ts @@ -12,9 +12,9 @@ limitations under the License. */ export enum DaprPubSubStatusEnum { - SUCCESS = 'SUCCESS', // Message is processed successfully - RETRY = 'RETRY', // Message to be retried by Dapr - DROP = 'DROP', // Warning is logged and message is dropped + SUCCESS = 'SUCCESS', // Message is processed successfully + RETRY = 'RETRY', // Message to be retried by Dapr + DROP = 'DROP', // Warning is logged and message is dropped } export default DaprPubSubStatusEnum diff --git a/packages/dapr-sdk/src/types/pubsub/KeyValue.type.ts b/packages/dapr-sdk/src/types/pubsub/KeyValue.type.ts index 1ac58e202..702a9a6fe 100644 --- a/packages/dapr-sdk/src/types/pubsub/KeyValue.type.ts +++ b/packages/dapr-sdk/src/types/pubsub/KeyValue.type.ts @@ -12,5 +12,5 @@ limitations under the License. */ export type KeyValueType = { - [key: string]: unknown + [key: string]: unknown } diff --git a/packages/dapr-sdk/src/types/pubsub/PubSubBulkPublishApiResponse.type.ts b/packages/dapr-sdk/src/types/pubsub/PubSubBulkPublishApiResponse.type.ts index 6ca54e360..29b7d3167 100644 --- a/packages/dapr-sdk/src/types/pubsub/PubSubBulkPublishApiResponse.type.ts +++ b/packages/dapr-sdk/src/types/pubsub/PubSubBulkPublishApiResponse.type.ts @@ -12,13 +12,13 @@ limitations under the License. */ type PubSubBulkPublishApiResponseStatus = { - entryID: string - error: string + entryID: string + error: string } /** * Response from a bulk publish API request. */ export type PubSubBulkPublishApiResponse = { - failedEntries: PubSubBulkPublishApiResponseStatus[] + failedEntries: PubSubBulkPublishApiResponseStatus[] } diff --git a/packages/dapr-sdk/src/types/pubsub/PubSubBulkPublishEntry.type.ts b/packages/dapr-sdk/src/types/pubsub/PubSubBulkPublishEntry.type.ts index 860f2f619..6431189dd 100644 --- a/packages/dapr-sdk/src/types/pubsub/PubSubBulkPublishEntry.type.ts +++ b/packages/dapr-sdk/src/types/pubsub/PubSubBulkPublishEntry.type.ts @@ -17,8 +17,8 @@ import type { KeyValueType } from './KeyValue.type.js' * PubSubBulkPublishEntry defines an entry in a bulk publish request. */ export type PubSubBulkPublishEntry = { - entryID: string - event: object | string - metadata: KeyValueType - contentType: string + entryID: string + event: object | string + metadata: KeyValueType + contentType: string } diff --git a/packages/dapr-sdk/src/types/pubsub/PubSubBulkPublishMessage.type.ts b/packages/dapr-sdk/src/types/pubsub/PubSubBulkPublishMessage.type.ts index 62a313e05..1f0b3a0dc 100644 --- a/packages/dapr-sdk/src/types/pubsub/PubSubBulkPublishMessage.type.ts +++ b/packages/dapr-sdk/src/types/pubsub/PubSubBulkPublishMessage.type.ts @@ -14,10 +14,10 @@ limitations under the License. import type { KeyValueType } from './KeyValue.type.js' type PubSubBulkPublishMessageExplicit = { - entryID?: string - event: object | string - metadata?: KeyValueType - contentType?: string + entryID?: string + event: object | string + metadata?: KeyValueType + contentType?: string } /** diff --git a/packages/dapr-sdk/src/types/pubsub/PubSubBulkPublishResponse.type.ts b/packages/dapr-sdk/src/types/pubsub/PubSubBulkPublishResponse.type.ts index f8037f33c..a12ec2064 100644 --- a/packages/dapr-sdk/src/types/pubsub/PubSubBulkPublishResponse.type.ts +++ b/packages/dapr-sdk/src/types/pubsub/PubSubBulkPublishResponse.type.ts @@ -14,13 +14,13 @@ limitations under the License. import type { PubSubBulkPublishEntry } from './PubSubBulkPublishEntry.type.js' type PubSubBulkPublishResponseFailedEntry = { - message: PubSubBulkPublishEntry - error: Error + message: PubSubBulkPublishEntry + error: Error } /** * PubSubBulkPublishResponse defines the response from a bulk publish request. */ export type PubSubBulkPublishResponse = { - failedMessages: PubSubBulkPublishResponseFailedEntry[] + failedMessages: PubSubBulkPublishResponseFailedEntry[] } diff --git a/packages/dapr-sdk/src/types/pubsub/PubSubPublishOptions.type.ts b/packages/dapr-sdk/src/types/pubsub/PubSubPublishOptions.type.ts index 1363ee6d1..026a97e58 100644 --- a/packages/dapr-sdk/src/types/pubsub/PubSubPublishOptions.type.ts +++ b/packages/dapr-sdk/src/types/pubsub/PubSubPublishOptions.type.ts @@ -14,14 +14,14 @@ limitations under the License. import type { KeyValueType } from './KeyValue.type.js' export type PubSubPublishOptions = { - /** - * The content type of the message. - * This is optional and will be inferred from the payload if not provided. - */ - contentType?: string + /** + * The content type of the message. + * This is optional and will be inferred from the payload if not provided. + */ + contentType?: string - /** - * Metadata to be passed to the publish operation. - */ - metadata?: KeyValueType + /** + * Metadata to be passed to the publish operation. + */ + metadata?: KeyValueType } diff --git a/packages/dapr-sdk/src/types/pubsub/PubSubPublishResponse.type.ts b/packages/dapr-sdk/src/types/pubsub/PubSubPublishResponse.type.ts index 5e52a2b71..bc26061b9 100644 --- a/packages/dapr-sdk/src/types/pubsub/PubSubPublishResponse.type.ts +++ b/packages/dapr-sdk/src/types/pubsub/PubSubPublishResponse.type.ts @@ -15,6 +15,6 @@ limitations under the License. * PubSubPublishResponseType defines the response from a publish. */ export type PubSubPublishResponseType = { - // error contains the error if the publish failed. - error?: Error + // error contains the error if the publish failed. + error?: Error } diff --git a/packages/dapr-sdk/src/types/pubsub/PubSubSubscription.type.ts b/packages/dapr-sdk/src/types/pubsub/PubSubSubscription.type.ts index 6bd021bf4..379bb9a08 100644 --- a/packages/dapr-sdk/src/types/pubsub/PubSubSubscription.type.ts +++ b/packages/dapr-sdk/src/types/pubsub/PubSubSubscription.type.ts @@ -17,6 +17,6 @@ import type { PubSubSubscriptionTopicType } from './PubSubSubscriptionTopic.type * This defines the pubsubName object */ export type PubSubSubscriptionType = { - // Key of the topic - [key: string]: PubSubSubscriptionTopicType + // Key of the topic + [key: string]: PubSubSubscriptionTopicType } diff --git a/packages/dapr-sdk/src/types/pubsub/PubSubSubscriptionOptions.type.ts b/packages/dapr-sdk/src/types/pubsub/PubSubSubscriptionOptions.type.ts index ef16e2287..7049d191f 100644 --- a/packages/dapr-sdk/src/types/pubsub/PubSubSubscriptionOptions.type.ts +++ b/packages/dapr-sdk/src/types/pubsub/PubSubSubscriptionOptions.type.ts @@ -20,21 +20,21 @@ import type { KeyValueType } from './KeyValue.type.js' * PubSubSubscriptionOptionsType defines the options we can pass while subscribing */ export type PubSubSubscriptionOptionsType = { - // Metadata - metadata?: KeyValueType + // Metadata + metadata?: KeyValueType - // The deadletter topic path - deadLetterTopic?: string + // The deadletter topic path + deadLetterTopic?: string - // The deadletter callback to call - deadLetterCallback?: TypeDaprPubSubCallback + // The deadletter callback to call + deadLetterCallback?: TypeDaprPubSubCallback - // The default callback - callback?: TypeDaprPubSubCallback + // The default callback + callback?: TypeDaprPubSubCallback - // The route creation for a single route or DaprPubSubRouteType - route?: string | DaprPubSubRouteType + // The route creation for a single route or DaprPubSubRouteType + route?: string | DaprPubSubRouteType - // The settings for bulk subscribe - bulkSubscribe?: BulkSubscribeConfig + // The settings for bulk subscribe + bulkSubscribe?: BulkSubscribeConfig } diff --git a/packages/dapr-sdk/src/types/pubsub/PubSubSubscriptionTopic.type.ts b/packages/dapr-sdk/src/types/pubsub/PubSubSubscriptionTopic.type.ts index b4c13566f..2a95322c9 100644 --- a/packages/dapr-sdk/src/types/pubsub/PubSubSubscriptionTopic.type.ts +++ b/packages/dapr-sdk/src/types/pubsub/PubSubSubscriptionTopic.type.ts @@ -18,9 +18,9 @@ import type { PubSubSubscriptionTopicRoutesType } from './PubSubSubscriptionTopi * This defines the topicName object */ export type PubSubSubscriptionTopicType = { - // The routes defined in a topic - routes: PubSubSubscriptionTopicRoutesType + // The routes defined in a topic + routes: PubSubSubscriptionTopicRoutesType - // dapr is configured on topic level - dapr: DaprPubSubType + // dapr is configured on topic level + dapr: DaprPubSubType } diff --git a/packages/dapr-sdk/src/types/pubsub/PubSubSubscriptionTopicRoute.type.ts b/packages/dapr-sdk/src/types/pubsub/PubSubSubscriptionTopicRoute.type.ts index 9cdc2b5e3..8cbbf40a7 100644 --- a/packages/dapr-sdk/src/types/pubsub/PubSubSubscriptionTopicRoute.type.ts +++ b/packages/dapr-sdk/src/types/pubsub/PubSubSubscriptionTopicRoute.type.ts @@ -17,9 +17,9 @@ import type { TypeDaprPubSubCallback } from './DaprPubSubCallback.type.js' * This defines the routeName object */ export type PubSubSubscriptionTopicRouteType = { - // Our event handlers - eventHandlers: TypeDaprPubSubCallback[] + // Our event handlers + eventHandlers: TypeDaprPubSubCallback[] - // The path on the server (e.g. POST /my-path) - path: string + // The path on the server (e.g. POST /my-path) + path: string } diff --git a/packages/dapr-sdk/src/types/pubsub/PubSubSubscriptionTopicRoutes.type.ts b/packages/dapr-sdk/src/types/pubsub/PubSubSubscriptionTopicRoutes.type.ts index 54e2789ab..fe4b88d48 100644 --- a/packages/dapr-sdk/src/types/pubsub/PubSubSubscriptionTopicRoutes.type.ts +++ b/packages/dapr-sdk/src/types/pubsub/PubSubSubscriptionTopicRoutes.type.ts @@ -17,6 +17,6 @@ import type { PubSubSubscriptionTopicRouteType } from './PubSubSubscriptionTopic * This defines the routes object */ export type PubSubSubscriptionTopicRoutesType = { - // The key of the route - [key: string]: PubSubSubscriptionTopicRouteType + // The key of the route + [key: string]: PubSubSubscriptionTopicRouteType } diff --git a/packages/dapr-sdk/src/types/pubsub/PubSubSubscriptions.type.ts b/packages/dapr-sdk/src/types/pubsub/PubSubSubscriptions.type.ts index dff762c3e..33c9412ca 100644 --- a/packages/dapr-sdk/src/types/pubsub/PubSubSubscriptions.type.ts +++ b/packages/dapr-sdk/src/types/pubsub/PubSubSubscriptions.type.ts @@ -17,6 +17,6 @@ import type { PubSubSubscriptionType } from './PubSubSubscription.type.js' * This defines the entire object containing pubsubNames */ export type PubSubSubscriptionsType = { - // The key of a pubsubName - [key: string]: PubSubSubscriptionType + // The key of a pubsubName + [key: string]: PubSubSubscriptionType } diff --git a/packages/dapr-sdk/tsconfig.json b/packages/dapr-sdk/tsconfig.json index 42dac6999..37678fdd4 100644 --- a/packages/dapr-sdk/tsconfig.json +++ b/packages/dapr-sdk/tsconfig.json @@ -1,21 +1,12 @@ { - "extends": "../../tsconfig.json", - "compilerOptions": { - "outDir": "./dist", - "declaration": true, - "sourceMap": false, - "declarationMap": true, - "types": [ - "vitest/globals", - "node" - ] - }, - "exclude": [ - "./**/*.d.ts" - ], - - "include": [ - "./src/**/*", - "./test/*", - ], -} \ No newline at end of file + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "./dist", + "declaration": true, + "sourceMap": false, + "declarationMap": true, + "types": ["vitest/globals", "node"] + }, + "exclude": ["./**/*.d.ts"], + "include": ["./src/**/*", "./test/*"] +} diff --git a/packages/dapr-sdk/typedoc.json b/packages/dapr-sdk/typedoc.json index 355bf0f98..71c4b2283 100644 --- a/packages/dapr-sdk/typedoc.json +++ b/packages/dapr-sdk/typedoc.json @@ -1,6 +1,5 @@ { - - "extends": ["../../typedoc.base.json"], - "entryPoints": ["src/index.ts"], - "tsconfig": "./tsconfig.json" -} \ No newline at end of file + "extends": ["../../typedoc.base.json"], + "entryPoints": ["src/index.ts"], + "tsconfig": "./tsconfig.json" +} diff --git a/packages/gcloud-secret-store/jsr.json b/packages/gcloud-secret-store/jsr.json new file mode 100644 index 000000000..9b57c3c0f --- /dev/null +++ b/packages/gcloud-secret-store/jsr.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://jsr.io/schema/config-file.v1.json", + "name": "@purista/gcloud-secret-store", + "version": "1.11.0", + "description": "State store adapter for Google Cloud Secret Manager", + "keywords": ["purista", "google", "cloud", "typescript", "javascript"], + "exports": "./dist/esm/index.js", + "publish": { + "include": ["dist/**/*.js", "dist/**/*.d.ts", "README.md", "package.json"], + "exclude": [ + "src", + ".github", + ".vscode", + ".zed", + "!dist", + "!dist/**/*.js", + "!dist/**/*.d.ts", + ".tshy", + ".tshy-build", + "vendor", + "docs", + "typedoc.json", + "..eslintcache", + ".npmignore" + ] + } +} diff --git a/packages/gcloud-secret-store/package.json b/packages/gcloud-secret-store/package.json index 5e2aabaef..7c68e0389 100644 --- a/packages/gcloud-secret-store/package.json +++ b/packages/gcloud-secret-store/package.json @@ -1,63 +1,60 @@ { - "name": "@purista/gcloud-secret-store", - "version": "1.11.0", - "description": "State store adapter for Google Cloud Secret Manager", - "homepage": "https://purista.dev", - "repository": { - "type": "git", - "url": "git@github.com:sebastianwessel/purista.git" - }, - "author": "Sebastian Wessel", - "license": "ISC", - "type": "module", - "main": "./dist/commonjs/index.js", - "exports": { - "./package.json": "./package.json", - ".": { - "import": { - "types": "./dist/esm/index.d.ts", - "default": "./dist/esm/index.js" - }, - "require": { - "types": "./dist/commonjs/index.d.ts", - "default": "./dist/commonjs/index.js" - } - } - }, - "files": [ - "dist/**/*" - ], - "publishConfig": { - "access": "public" - }, - "engines": { - "node": ">=18.15" - }, - "scripts": { - "lint": "eslint . --ext .ts,.json --cache . --fix", - "test": "vitest", - "build": "rimraf dist && tshy" - }, - "tshy": { - "exclude": [ - "src/**/*.test.ts" - ], - "exports": { - "./package.json": "./package.json", - ".": "./src/index.ts" - } - }, - "devDependencies": { - "@types/node": "^20.11.17", - "@types/sinon": "^17.0.3", - "sinon": "^17.0.1", - "tshy": "^1.11.1", - "vitest": "^1.3.0" - }, - "dependencies": { - "@google-cloud/secret-manager": "^5.1.0", - "@purista/core": "*" - }, - "peerDependenciesMeta": {}, - "types": "./dist/commonjs/index.d.ts" + "name": "@purista/gcloud-secret-store", + "version": "1.11.0", + "description": "State store adapter for Google Cloud Secret Manager", + "homepage": "https://purista.dev", + "repository": { + "type": "git", + "url": "git@github.com:puristajs/purista.git" + }, + "author": "Sebastian Wessel", + "license": "ISC", + "type": "module", + "main": "./dist/commonjs/index.js", + "exports": { + "./package.json": "./package.json", + ".": { + "import": { + "types": "./dist/esm/index.d.ts", + "default": "./dist/esm/index.js" + }, + "require": { + "types": "./dist/commonjs/index.d.ts", + "default": "./dist/commonjs/index.js" + } + } + }, + "files": ["dist/**/*"], + "publishConfig": { + "access": "public" + }, + "engines": { + "node": ">=18.15" + }, + "scripts": { + "lint": "npx @biomejs/biome check --write", + "test": "vitest", + "build": "rimraf dist && tshy" + }, + "tshy": { + "exclude": ["src/**/*.test.ts"], + "exports": { + "./package.json": "./package.json", + ".": "./src/index.ts" + } + }, + "devDependencies": { + "@types/node": "^22.5.1", + "@types/sinon": "^17.0.3", + "sinon": "^19.0.2", + "tshy": "^3.0.2", + "vitest": "^3.0.4" + }, + "dependencies": { + "@google-cloud/secret-manager": "^5.6.0", + "@purista/core": "*" + }, + "peerDependenciesMeta": {}, + "types": "./dist/commonjs/index.d.ts", + "module": "./dist/esm/index.js" } diff --git a/packages/gcloud-secret-store/src/GoogleSecretStore.impl.ts b/packages/gcloud-secret-store/src/GoogleSecretStore.impl.ts index ff11957d4..b0acf1684 100644 --- a/packages/gcloud-secret-store/src/GoogleSecretStore.impl.ts +++ b/packages/gcloud-secret-store/src/GoogleSecretStore.impl.ts @@ -2,11 +2,11 @@ import { join } from 'node:path/posix' import { SecretManagerServiceClient } from '@google-cloud/secret-manager' import { - type ObjectWithKeysFromStringArray, - SecretStoreBaseClass, - StatusCode, - type StoreBaseConfig, - UnhandledError, + type ObjectWithKeysFromStringArray, + SecretStoreBaseClass, + StatusCode, + type StoreBaseConfig, + UnhandledError, } from '@purista/core' import type { GoogleSecretStoreConfig } from './types.js' @@ -25,58 +25,58 @@ import type { GoogleSecretStoreConfig } from './types.js' * It will be removed/overwritten on next get request. */ export class GoogleSecretStore extends SecretStoreBaseClass { - client: SecretManagerServiceClient + client: SecretManagerServiceClient - constructor(config: StoreBaseConfig) { - super('GoogleSecretStore', { enableCache: true, ...config }) - this.client = new SecretManagerServiceClient(this.config.client) - } + constructor(config: StoreBaseConfig) { + super('GoogleSecretStore', { enableCache: true, ...config }) + this.client = new SecretManagerServiceClient(this.config.client) + } - protected async getSecretImpl( - ...secretNames: SecretNames - ): Promise> { - const result: Record = {} + protected async getSecretImpl( + ...secretNames: SecretNames + ): Promise> { + const result: Record = {} - for (const name of secretNames) { - result[name] = undefined - try { - const res = await this.client.accessSecretVersion({ - name: join(this.config.project, 'secrets', name, 'versions', 'latest'), - }) - result[name] = res[0].payload?.data?.toString() - } catch (err) { - this.logger.error({ err }) - throw UnhandledError.fromError(err, StatusCode.InternalServerError) - } - } + for (const name of secretNames) { + result[name] = undefined + try { + const res = await this.client.accessSecretVersion({ + name: join(this.config.project, 'secrets', name, 'versions', 'latest'), + }) + result[name] = res[0].payload?.data?.toString() + } catch (err) { + this.logger.error({ err }) + throw UnhandledError.fromError(err, StatusCode.InternalServerError) + } + } - return result as ObjectWithKeysFromStringArray - } + return result as ObjectWithKeysFromStringArray + } - async removeSecretImpl(secretName: string) { - await this.client.deleteSecret({ name: join(this.config.project, 'secrets', secretName) }) - } + async removeSecretImpl(secretName: string) { + await this.client.deleteSecret({ name: join(this.config.project, 'secrets', secretName) }) + } - async setSecretImpl(secretName: string, secretValue: string) { - const existingValue = await this.getSecret(secretName) + async setSecretImpl(secretName: string, secretValue: string) { + const existingValue = await this.getSecret(secretName) - if (!existingValue[secretName]) { - await this.client.createSecret({ - parent: this.config.project, - secretId: secretName, - secret: { - replication: { - automatic: {}, - }, - }, - }) - } + if (!existingValue[secretName]) { + await this.client.createSecret({ + parent: this.config.project, + secretId: secretName, + secret: { + replication: { + automatic: {}, + }, + }, + }) + } - await this.client.addSecretVersion({ - parent: join(this.config.project, 'secrets', secretName), - payload: { - data: Buffer.from(secretValue, 'utf8'), - }, - }) - } + await this.client.addSecretVersion({ + parent: join(this.config.project, 'secrets', secretName), + payload: { + data: Buffer.from(secretValue, 'utf8'), + }, + }) + } } diff --git a/packages/gcloud-secret-store/src/index.test.ts b/packages/gcloud-secret-store/src/index.test.ts index f109d3c6f..52b34bb05 100644 --- a/packages/gcloud-secret-store/src/index.test.ts +++ b/packages/gcloud-secret-store/src/index.test.ts @@ -1,11 +1,11 @@ import { GoogleSecretStore, puristaVersion } from './index.js' describe('exports GoogleSecretStore', () => { - it('has a version', () => { - expect(puristaVersion).toBeDefined() - }) + it('has a version', () => { + expect(puristaVersion).toBeDefined() + }) - it('exports GoogleSecretStore', () => { - expect(GoogleSecretStore).toBeDefined() - }) + it('exports GoogleSecretStore', () => { + expect(GoogleSecretStore).toBeDefined() + }) }) diff --git a/packages/gcloud-secret-store/src/types.ts b/packages/gcloud-secret-store/src/types.ts index 587cfb092..31905177e 100644 --- a/packages/gcloud-secret-store/src/types.ts +++ b/packages/gcloud-secret-store/src/types.ts @@ -4,13 +4,13 @@ import type { ClientOptions } from 'google-gax' * Google Secret Manager store config */ export type GoogleSecretStoreConfig = { - /** - * The google project id in format of projects/* without ending /secrets - * @example projects/428371962963 - */ - project: string - /** - * Google client options - */ - client?: ClientOptions + /** + * The google project id in format of projects/* without ending /secrets + * @example projects/428371962963 + */ + project: string + /** + * Google client options + */ + client?: ClientOptions } diff --git a/packages/gcloud-secret-store/test/integration.test.ts b/packages/gcloud-secret-store/test/integration.test.ts index 133502f00..77dc5c507 100644 --- a/packages/gcloud-secret-store/test/integration.test.ts +++ b/packages/gcloud-secret-store/test/integration.test.ts @@ -2,35 +2,45 @@ import { randomUUID } from 'node:crypto' import { getLoggerMock } from '@purista/core' +import path from 'node:path' +import { fileURLToPath } from 'node:url' import { GoogleSecretStore } from '../src/GoogleSecretStore.impl.js' -describe.skip('Google Secret Manager secret store', () => { - const secretName = randomUUID() - - const store = new GoogleSecretStore({ - project: 'projects/428371962963', - enableGet: true, - enableRemove: true, - enableSet: true, - logger: getLoggerMock().mock, - }) - - it('set a secret key', async () => { - await expect(store.setSecret(secretName, 'my-value')).resolves.toBeUndefined() - }) - - it('gets a secret key', async () => { - await expect(store.getSecret(secretName)).resolves.toStrictEqual({ [secretName]: 'my-value' }) - }) - - it('updates a secret key', async () => { - await expect(store.setSecret(secretName, 'my-value-updated')).resolves.toBeUndefined() - await expect(store.getSecret(secretName)).resolves.toStrictEqual({ [secretName]: 'my-value-updated' }) - }) - - it('removes a secret key', async () => { - await expect(store.getSecret(secretName)).resolves.toStrictEqual({ [secretName]: 'my-value-updated' }) - await expect(store.removeSecret(secretName)).resolves.toBeUndefined() - await expect(store.getSecret(secretName)).resolves.toStrictEqual({ [secretName]: undefined }) - }) +describe('Google Secret Manager secret store', () => { + const secretName = randomUUID() + + const __filename = fileURLToPath(import.meta.url) + const __dirname = path.dirname(__filename) + + const keyFilename = path.join(__dirname, './gcloud-credentials.json') + + const store = new GoogleSecretStore({ + client: { + keyFilename, + }, + project: 'projects/428371962963', + enableGet: true, + enableRemove: true, + enableSet: true, + logger: getLoggerMock().mock, + }) + + it('set a secret key', async () => { + await expect(store.setSecret(secretName, 'my-value')).resolves.toBeUndefined() + }) + + it('gets a secret key', async () => { + await expect(store.getSecret(secretName)).resolves.toStrictEqual({ [secretName]: 'my-value' }) + }) + + it('updates a secret key', async () => { + await expect(store.setSecret(secretName, 'my-value-updated')).resolves.toBeUndefined() + await expect(store.getSecret(secretName)).resolves.toStrictEqual({ [secretName]: 'my-value-updated' }) + }) + + it('removes a secret key', async () => { + await expect(store.getSecret(secretName)).resolves.toStrictEqual({ [secretName]: 'my-value-updated' }) + await expect(store.removeSecret(secretName)).resolves.toBeUndefined() + await expect(store.getSecret(secretName)).resolves.toStrictEqual({ [secretName]: undefined }) + }) }) diff --git a/packages/gcloud-secret-store/tsconfig.json b/packages/gcloud-secret-store/tsconfig.json index 42dac6999..a2f689e2f 100644 --- a/packages/gcloud-secret-store/tsconfig.json +++ b/packages/gcloud-secret-store/tsconfig.json @@ -1,21 +1,13 @@ { - "extends": "../../tsconfig.json", - "compilerOptions": { - "outDir": "./dist", - "declaration": true, - "sourceMap": false, - "declarationMap": true, - "types": [ - "vitest/globals", - "node" - ] - }, - "exclude": [ - "./**/*.d.ts" - ], - - "include": [ - "./src/**/*", - "./test/*", - ], -} \ No newline at end of file + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "./dist", + "declaration": true, + "sourceMap": false, + "declarationMap": true, + "types": ["vitest/globals", "node"] + }, + "exclude": ["./**/*.d.ts"], + + "include": ["./src/**/*", "./test/*"] +} diff --git a/packages/gcloud-secret-store/typedoc.json b/packages/gcloud-secret-store/typedoc.json index 355bf0f98..71c4b2283 100644 --- a/packages/gcloud-secret-store/typedoc.json +++ b/packages/gcloud-secret-store/typedoc.json @@ -1,6 +1,5 @@ { - - "extends": ["../../typedoc.base.json"], - "entryPoints": ["src/index.ts"], - "tsconfig": "./tsconfig.json" -} \ No newline at end of file + "extends": ["../../typedoc.base.json"], + "entryPoints": ["src/index.ts"], + "tsconfig": "./tsconfig.json" +} diff --git a/packages/hono-http-server/jsr.json b/packages/hono-http-server/jsr.json new file mode 100644 index 000000000..208fee484 --- /dev/null +++ b/packages/hono-http-server/jsr.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://jsr.io/schema/config-file.v1.json", + "name": "@purista/hono-http-server", + "version": "1.11.0", + "description": "A http server service based on Hono for PURISTA backend framework", + "keywords": ["purista", "http", "server", "hono", "typescript", "javascript"], + "exports": "./dist/esm/index.js", + "publish": { + "include": ["dist/**/*.js", "dist/**/*.d.ts", "README.md", "package.json"], + "exclude": [ + "src", + ".github", + ".vscode", + ".zed", + "!dist", + "!dist/**/*.js", + "!dist/**/*.d.ts", + ".tshy", + ".tshy-build", + "vendor", + "docs", + "typedoc.json", + "..eslintcache", + ".npmignore" + ] + } +} diff --git a/packages/hono-http-server/package.json b/packages/hono-http-server/package.json index c150b32b2..9c0b300d9 100644 --- a/packages/hono-http-server/package.json +++ b/packages/hono-http-server/package.json @@ -1,70 +1,68 @@ { - "name": "@purista/hono-http-server", - "version": "1.11.0", - "description": "A http server service based on Hono for PURISTA backend framework", - "homepage": "https://purista.dev", - "repository": { - "type": "git", - "url": "git@github.com:sebastianwessel/purista.git" - }, - "author": "Sebastian Wessel", - "license": "ISC", - "type": "module", - "main": "./dist/commonjs/index.js", - "exports": { - "./package.json": "./package.json", - ".": { - "import": { - "types": "./dist/esm/index.d.ts", - "default": "./dist/esm/index.js" - }, - "require": { - "types": "./dist/commonjs/index.d.ts", - "default": "./dist/commonjs/index.js" - } - } - }, - "files": [ - "dist/**/*" - ], - "publishConfig": { - "access": "public" - }, - "engines": { - "node": ">=18.15" - }, - "scripts": { - "lint": "eslint . --ext .ts,.json --cache . --fix", - "test": "vitest", - "build": "rimraf dist && tshy" - }, - "tshy": { - "exclude": [ - "src/**/*.test.ts" - ], - "exports": { - "./package.json": "./package.json", - ".": "./src/index.ts" - } - }, - "devDependencies": { - "@types/node": "^20.11.17", - "@types/sinon": "^17.0.3", - "@hono/swagger-ui": "^0.2.1", - "sinon": "^17.0.1", - "tshy": "^1.11.1", - "vitest": "^1.3.0" - }, - "dependencies": { - "@opentelemetry/api": "^1.7.0", - "@opentelemetry/semantic-conventions": "^1.19.0", - "@purista/core": "*", - "hono": "^4.0.4" - }, - "peerDependenciesMeta": {}, - "peerDependencies": { - "@hono/node-server": "^1.8.0", - "@hono/swagger-ui": "^0.2.1" - }, - "types": "./dist/commonjs/index.d.ts" + "name": "@purista/hono-http-server", + "version": "1.11.0", + "description": "A http server service based on Hono for PURISTA backend framework", + "homepage": "https://purista.dev", + "repository": { + "type": "git", + "url": "git@github.com:puristajs/purista.git" + }, + "author": "Sebastian Wessel", + "license": "ISC", + "type": "module", + "main": "./dist/commonjs/index.js", + "exports": { + "./package.json": "./package.json", + ".": { + "import": { + "types": "./dist/esm/index.d.ts", + "default": "./dist/esm/index.js" + }, + "require": { + "types": "./dist/commonjs/index.d.ts", + "default": "./dist/commonjs/index.js" + } + } + }, + "files": ["dist/**/*"], + "publishConfig": { + "access": "public" + }, + "engines": { + "node": ">=18.15" + }, + "scripts": { + "lint": "npx @biomejs/biome check --write", + "test": "vitest", + "build": "rimraf dist && tshy" + }, + "tshy": { + "exclude": ["src/**/*.test.ts"], + "exports": { + "./package.json": "./package.json", + ".": "./src/index.ts" + } + }, + "devDependencies": { + "@types/node": "^22.5.1", + "@types/sinon": "^17.0.3", + "@hono/swagger-ui": "^0.5.0", + "sinon": "^19.0.2", + "tshy": "^3.0.2", + "vitest": "^3.0.4" + }, + "dependencies": { + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/semantic-conventions": "^1.27.0", + "@purista/core": "*", + "hono": "^4.4.7" + }, + "peerDependenciesMeta": {}, + "peerDependencies": { + "@hono/node-server": "^1.8.0", + "@hono/swagger-ui": "^0.3.0", + "@scalar/hono-api-reference": "^0.5.119" + }, + "types": "./dist/commonjs/index.d.ts", + "module": "./dist/esm/index.js" } diff --git a/packages/hono-http-server/src/helper/addPathToOpenApi.ts b/packages/hono-http-server/src/helper/addPathToOpenApi.ts index 46d865b95..6e6d38e81 100644 --- a/packages/hono-http-server/src/helper/addPathToOpenApi.ts +++ b/packages/hono-http-server/src/helper/addPathToOpenApi.ts @@ -8,123 +8,124 @@ import { getParameterDefinition } from './getParameterDefinition.js' import { getQueryDefintion } from './getQueryDefintion.js' type Config = { - traceHeaderField?: string + traceHeaderField?: string } export const addPathToOpenApi = ( - openApiBuilder: OpenApiBuilder, - metadata: HttpExposedServiceMeta, - path: string, - config: Config, + openApiBuilder: OpenApiBuilder, + metadata: HttpExposedServiceMeta, + path: string, + config: Config, ) => { - const expose = metadata.expose - - const method = expose.http.method.toLowerCase() as 'put' | 'post' | 'patch' | 'get' | 'delete' - - const requestContentType = expose.contentTypeRequest ?? 'application/json' - const _requestEncodingType = expose.contentEncodingRequest ?? 'utf-8' - - const responseContentType = expose.contentTypeResponse ?? 'application/json' - const responseEncodingType = expose.contentEncodingResponse ?? 'utf-8' - - const traceIdParameter: ParameterObject = { - in: 'header', - required: false, - name: config.traceHeaderField ?? 'x-trace-id', - schema: { type: 'string' }, - example: '022bcd32-0a7c-4635-90ce-7940d0b9793f', - description: 'TraceID which can be used by business logic', - } - - const traceParent: ParameterObject = { - in: 'header', - required: false, - name: 'traceparent', - schema: { type: 'string' }, - description: 'see: https://www.w3.org/TR/trace-context/#traceparent-header-field-values', - } - - const securitySchema = Object.keys(openApiBuilder.rootDoc.components?.securitySchemes ?? {}).map((name) => ({ - [name]: [], - })) - - const okCode = expose.outputPayload?.type ? StatusCode.OK : StatusCode.NoContent - - const errorCodes: Set = new Set([...(expose.http.openApi?.additionalStatusCodes ?? [])]) - - if (expose.http.openApi?.isSecure) { - errorCodes.add(StatusCode.Unauthorized) - } - - if (expose.inputPayload?.type) { - errorCodes.add(StatusCode.BadRequest) - } - - const errArray = Array.from(errorCodes).sort((a, b) => a - b) - - errArray.forEach((code) => { - openApiBuilder.addSchema(`error_${code}_schema`, getErrorResponseSchema(code, getErrorName(code))) - }) - - const errResponses = errArray.reduce((prev, code) => { - return { - ...prev, - [`${code}`]: { - description: getErrorName(code), - content: { - 'application/json': { - schema: { - $ref: `#/components/schemas/error_${code}_schema`, - }, - }, - }, - }, - } - }, {} as ResponsesObject) - - const operation: OperationObject = { - tags: expose.http.openApi?.tags, - summary: expose.http.openApi?.summary, - description: expose.http.openApi?.description, - deprecated: expose.deprecated, - operationId: expose.http.openApi?.operationId, - security: securitySchema.length > 0 && expose.http.openApi?.isSecure ? securitySchema : [], - parameters: [ - ...getParameterDefinition(path, expose.parameter), - ...getQueryDefintion(expose.http.openApi?.query, expose.parameter), - traceIdParameter, - traceParent, - ], - requestBody: { - content: { - [requestContentType]: { - schema: method !== 'get' ? expose.inputPayload : undefined, - }, - }, - }, - responses: { - [`${okCode}`]: { - description: getErrorName(okCode), - content: - okCode === StatusCode.NoContent - ? undefined - : { - [responseContentType]: { - schema: expose.outputPayload, - encoding: responseEncodingType, - }, - }, - }, - ...errResponses, - }, - } - - const pathConverted = path - .split('/') - .map((part) => (part.startsWith(':') ? `{${part.replace(':', '').replace('?', '')}}` : part)) - .join('/') - - openApiBuilder.addPath(pathConverted, { - [method]: operation, - }) + const expose = metadata.expose + + const method = expose.http.method.toLowerCase() as 'put' | 'post' | 'patch' | 'get' | 'delete' + + const requestContentType = expose.contentTypeRequest ?? 'application/json' + const _requestEncodingType = expose.contentEncodingRequest ?? 'utf-8' + + const responseContentType = expose.contentTypeResponse ?? 'application/json' + const responseEncodingType = expose.contentEncodingResponse ?? 'utf-8' + + const traceIdParameter: ParameterObject = { + in: 'header', + required: false, + name: config.traceHeaderField ?? 'x-trace-id', + schema: { type: 'string' }, + example: '022bcd32-0a7c-4635-90ce-7940d0b9793f', + description: 'TraceID which can be used by business logic', + } + + const traceParent: ParameterObject = { + in: 'header', + required: false, + name: 'traceparent', + schema: { type: 'string' }, + description: 'see: https://www.w3.org/TR/trace-context/#traceparent-header-field-values', + } + + const securitySchema = Object.keys(openApiBuilder.rootDoc.components?.securitySchemes ?? {}).map(name => ({ + [name]: [], + })) + + const okCode = expose.outputPayload?.type ? StatusCode.OK : StatusCode.NoContent + + const errorCodes: Set = new Set([...(expose.http.openApi?.additionalStatusCodes ?? [])]) + + if (expose.http.openApi?.isSecure) { + errorCodes.add(StatusCode.Unauthorized) + } + + if (expose.inputPayload?.type) { + errorCodes.add(StatusCode.BadRequest) + } + + const errArray = Array.from(errorCodes).sort((a, b) => a - b) + + for (const code of errArray) { + openApiBuilder.addSchema(`error_${code}_schema`, getErrorResponseSchema(code, getErrorName(code))) + } + + const errResponses = errArray.reduce((prev, code) => { + return { + // biome-ignore lint/performance/noAccumulatingSpread: + ...prev, + [`${code}`]: { + description: getErrorName(code), + content: { + 'application/json': { + schema: { + $ref: `#/components/schemas/error_${code}_schema`, + }, + }, + }, + }, + } + }, {} as ResponsesObject) + + const operation: OperationObject = { + tags: expose.http.openApi?.tags, + summary: expose.http.openApi?.summary, + description: expose.http.openApi?.description, + deprecated: expose.deprecated, + operationId: expose.http.openApi?.operationId, + security: securitySchema.length > 0 && expose.http.openApi?.isSecure ? securitySchema : [], + parameters: [ + ...getParameterDefinition(path, expose.parameter), + ...getQueryDefintion(expose.http.openApi?.query, expose.parameter), + traceIdParameter, + traceParent, + ], + requestBody: { + content: { + [requestContentType]: { + schema: method !== 'get' ? expose.inputPayload : undefined, + }, + }, + }, + responses: { + [`${okCode}`]: { + description: getErrorName(okCode), + content: + okCode === StatusCode.NoContent + ? undefined + : { + [responseContentType]: { + schema: expose.outputPayload, + encoding: responseEncodingType, + }, + }, + }, + ...errResponses, + }, + } + + const pathConverted = path + .split('/') + .map(part => (part.startsWith(':') ? `{${part.replace(':', '').replace('?', '')}}` : part)) + .join('/') + + openApiBuilder.addPath(pathConverted, { + [method]: operation, + }) } diff --git a/packages/hono-http-server/src/helper/getErrorName.ts b/packages/hono-http-server/src/helper/getErrorName.ts index 7971e477d..ca0147626 100644 --- a/packages/hono-http-server/src/helper/getErrorName.ts +++ b/packages/hono-http-server/src/helper/getErrorName.ts @@ -1,10 +1,10 @@ import { StatusCode } from '@purista/core' export const getErrorName = (code: StatusCode) => - StatusCode[code] - .replace(/[A-Z]/g, (letter) => ` ${letter}`) - .replace(/^./, (str) => { - return str.toUpperCase() - }) - .trim() - .replace(/^O K$/g, 'OK') + StatusCode[code] + .replace(/[A-Z]/g, letter => ` ${letter}`) + .replace(/^./, str => { + return str.toUpperCase() + }) + .trim() + .replace(/^O K$/g, 'OK') diff --git a/packages/hono-http-server/src/helper/getErrorResponseSchema.ts b/packages/hono-http-server/src/helper/getErrorResponseSchema.ts index 7fd50f516..57744c1ab 100644 --- a/packages/hono-http-server/src/helper/getErrorResponseSchema.ts +++ b/packages/hono-http-server/src/helper/getErrorResponseSchema.ts @@ -2,91 +2,91 @@ import { StatusCode } from '@purista/core' import type { SchemaObject } from 'openapi3-ts/oas31' const inputValidationFailed: SchemaObject = { - type: 'array', - items: { - type: 'object', - properties: { - validation: { - type: 'string', - example: 'invalid_string', - }, - code: { - type: 'string', - example: 'invalid_string', - }, - message: { - type: 'string', - example: 'String must contain at least 3 character(s)', - }, - expected: { - type: 'string', - example: 'string', - }, - received: { - type: 'string', - example: 'object', - }, - keys: { - type: 'array', - items: { - type: 'string', - }, - }, - minimum: { - type: 'number', - example: 3, - }, - maximum: { - type: 'number', - example: 32, - }, - path: { - type: 'array', - items: { - type: 'string', - example: 'username', - }, - }, - }, - required: ['message', 'code'], - }, + type: 'array', + items: { + type: 'object', + properties: { + validation: { + type: 'string', + example: 'invalid_string', + }, + code: { + type: 'string', + example: 'invalid_string', + }, + message: { + type: 'string', + example: 'String must contain at least 3 character(s)', + }, + expected: { + type: 'string', + example: 'string', + }, + received: { + type: 'string', + example: 'object', + }, + keys: { + type: 'array', + items: { + type: 'string', + }, + }, + minimum: { + type: 'number', + example: 3, + }, + maximum: { + type: 'number', + example: 32, + }, + path: { + type: 'array', + items: { + type: 'string', + example: 'username', + }, + }, + }, + required: ['message', 'code'], + }, } export const getErrorResponseSchema = (code: StatusCode, message: string, schema?: SchemaObject): SchemaObject => { - const obj: SchemaObject = { - type: 'object', - properties: { - status: { - type: 'number', - minimum: 100, - title: 'the error status code', - example: code, - }, - message: { - type: 'string', - title: 'the error message', - example: message, - }, - traceId: { - type: 'string', - title: 'trace id', - example: 'd5dbb17eec16e3c9fce9cf8adc766999', - }, - }, - required: ['status', 'message'], - } + const obj: SchemaObject = { + type: 'object', + properties: { + status: { + type: 'number', + minimum: 100, + title: 'the error status code', + example: code, + }, + message: { + type: 'string', + title: 'the error message', + example: message, + }, + traceId: { + type: 'string', + title: 'trace id', + example: 'd5dbb17eec16e3c9fce9cf8adc766999', + }, + }, + required: ['status', 'message'], + } - if (schema) { - obj.properties = { - ...obj.properties, - data: schema, - } - } else if (code === StatusCode.BadRequest) { - obj.properties = { - ...obj.properties, - data: inputValidationFailed, - } - } + if (schema) { + obj.properties = { + ...obj.properties, + data: schema, + } + } else if (code === StatusCode.BadRequest) { + obj.properties = { + ...obj.properties, + data: inputValidationFailed, + } + } - return obj + return obj } diff --git a/packages/hono-http-server/src/helper/getParameterDefinition.ts b/packages/hono-http-server/src/helper/getParameterDefinition.ts index 7828ed807..720387be7 100644 --- a/packages/hono-http-server/src/helper/getParameterDefinition.ts +++ b/packages/hono-http-server/src/helper/getParameterDefinition.ts @@ -4,38 +4,41 @@ import { isReferenceObject } from 'openapi3-ts/oas31' const findPathParamsRegex = /:[^:/]+/gm export const getParameterDefinition = (path: string, parameterschema?: SchemaObject): ParameterObject[] => { - const routeParams: string[] = [] - let m: RegExpExecArray | null - - while ((m = findPathParamsRegex.exec(path)) !== null) { - if (m.index === findPathParamsRegex.lastIndex) { - findPathParamsRegex.lastIndex++ - } - - routeParams.push(...m.map((name) => name)) - } - - return routeParams.map((pathParamName) => { - const name = pathParamName.replace('?', '').replace(':', '') - const required = !pathParamName.endsWith('?') - - const schema = parameterschema?.properties?.[name] - - if (!!schema && isReferenceObject(schema)) { - return { - in: 'path', - name, - required, - ...schema, - } - } - - return { - in: 'path', - name, - required, - schema, - description: schema?.description ?? schema?.title, - } - }) + const routeParams: string[] = [] + let m: RegExpExecArray | null + + while (true) { + m = findPathParamsRegex.exec(path) + if (m === null) { + break + } + if (m.index === findPathParamsRegex.lastIndex) { + findPathParamsRegex.lastIndex++ + } + routeParams.push(...m.map(name => name)) + } + + return routeParams.map(pathParamName => { + const name = pathParamName.replace('?', '').replace(':', '') + const required = !pathParamName.endsWith('?') + + const schema = parameterschema?.properties?.[name] + + if (!!schema && isReferenceObject(schema)) { + return { + in: 'path', + name, + required, + ...schema, + } + } + + return { + in: 'path', + name, + required, + schema, + description: schema?.description ?? schema?.title, + } + }) } diff --git a/packages/hono-http-server/src/helper/getQueryDefintion.ts b/packages/hono-http-server/src/helper/getQueryDefintion.ts index 36450bccc..f6b191260 100644 --- a/packages/hono-http-server/src/helper/getQueryDefintion.ts +++ b/packages/hono-http-server/src/helper/getQueryDefintion.ts @@ -3,33 +3,33 @@ import type { ParameterObject, SchemaObject } from 'openapi3-ts/oas31' import { isReferenceObject } from 'openapi3-ts/oas31' export const getQueryDefintion = ( - queryDefinition: QueryParameter>[] | undefined, - parameterschema?: SchemaObject, + queryDefinition: QueryParameter>[] | undefined, + parameterschema?: SchemaObject, ): ParameterObject[] => { - if (!queryDefinition) { - return [] - } + if (!queryDefinition) { + return [] + } - return queryDefinition.map((queryParam) => { - const name = queryParam.name - const schema = parameterschema?.properties?.[name] - const required = queryParam.required + return queryDefinition.map(queryParam => { + const name = queryParam.name + const schema = parameterschema?.properties?.[name] + const required = queryParam.required - if (!!schema && isReferenceObject(schema)) { - return { - in: 'query', - name, - required, - ...schema, - } - } + if (!!schema && isReferenceObject(schema)) { + return { + in: 'query', + name, + required, + ...schema, + } + } - return { - in: 'query', - name, - required, - schema, - description: schema?.description ?? schema?.title, - } - }) + return { + in: 'query', + name, + required, + schema, + description: schema?.description ?? schema?.title, + } + }) } diff --git a/packages/hono-http-server/src/index.test.ts b/packages/hono-http-server/src/index.test.ts index d5803470f..96df7d6f6 100644 --- a/packages/hono-http-server/src/index.test.ts +++ b/packages/hono-http-server/src/index.test.ts @@ -1,11 +1,11 @@ import { honoV1Service, puristaVersion } from './index.js' describe('exports httpserver service', () => { - it('has a version', () => { - expect(puristaVersion).toBeDefined() - }) + it('has a version', () => { + expect(puristaVersion).toBeDefined() + }) - it('exports honoV1Service', () => { - expect(honoV1Service).toBeDefined() - }) + it('exports honoV1Service', () => { + expect(honoV1Service).toBeDefined() + }) }) diff --git a/packages/hono-http-server/src/service/hono/generalHonoServiceInfo.ts b/packages/hono-http-server/src/service/hono/generalHonoServiceInfo.ts index 650d92960..ed208afa4 100644 --- a/packages/hono-http-server/src/service/hono/generalHonoServiceInfo.ts +++ b/packages/hono-http-server/src/service/hono/generalHonoServiceInfo.ts @@ -1,6 +1,6 @@ import type { ServiceInfoType } from '@purista/core' export const generalHonoServiceInfo: Omit = { - serviceName: 'Hono', - serviceDescription: 'Provides a hono based web server for purista services', + serviceName: 'Hono', + serviceDescription: 'Provides a hono based web server for purista services', } diff --git a/packages/hono-http-server/src/service/hono/v1/HonoServiceClass.ts b/packages/hono-http-server/src/service/hono/v1/HonoServiceClass.ts index 97c9d6708..ad15edb64 100644 --- a/packages/hono-http-server/src/service/hono/v1/HonoServiceClass.ts +++ b/packages/hono-http-server/src/service/hono/v1/HonoServiceClass.ts @@ -1,14 +1,24 @@ import { posix } from 'node:path' -import { context, propagation, SpanKind, SpanStatusCode } from '@opentelemetry/api' -import { SemanticAttributes } from '@opentelemetry/semantic-conventions' -import type { Command, CommandDefinitionMetadataBase, EBMessageAddress, ServiceConstructorInput } from '@purista/core' -import { HandledError, isHttpExposedServiceMeta, safeBind, Service, StatusCode, UnhandledError } from '@purista/core' +import { SpanKind, SpanStatusCode, context, propagation } from '@opentelemetry/api' +import { ATTR_HTTP_ROUTE } from '@opentelemetry/semantic-conventions' + +import { ATTR_HTTP_METHOD, ATTR_HTTP_STATUS_CODE } from '@opentelemetry/semantic-conventions/incubating' + +import type { + Command, + CommandDefinitionMetadataBase, + EBMessageAddress, + EmptyObject, + ServiceClassTypes, + ServiceConstructorInput, +} from '@purista/core' +import { HandledError, Service, StatusCode, UnhandledError, isHttpExposedServiceMeta, safeBind } from '@purista/core' import type { Handler } from 'hono' import { Hono } from 'hono' import { HTTPException } from 'hono/http-exception' import { PatternRouter } from 'hono/router/pattern-router' -import type { StatusCode as HonoStatusCode } from 'hono/utils/http-status' +import type { ContentfulStatusCode } from 'hono/utils/http-status' import { OpenApiBuilder } from 'openapi3-ts/oas31' import { addPathToOpenApi } from '../../../helper/index.js' @@ -50,438 +60,438 @@ import type { HonoServiceV1Config } from './honoServiceConfig.js' * ``` */ export class HonoServiceClass< - Bindings extends BindingsBase = BindingsBase, - Variables extends VariablesBase = VariablesBase, -> extends Service { - /** - * The Hono instance - */ - public app - - /** - * The OpenApiBuilder instance - */ - public openApi: OpenApiBuilder - - private knownServices: Set = new Set() - - private isAvailable = false - - constructor(config: ServiceConstructorInput) { - super(config) - this.openApi = new OpenApiBuilder(this.config.openApi) - - if (this.config.enableDynamicRoutes) { - this.app = new Hono<{ Bindings: Bindings; Variables: Variables }>({ router: new PatternRouter() }) - } else { - this.app = new Hono<{ Bindings: Bindings; Variables: Variables }>() - this.subscriptionDefinitionList = [] - } - } - - /** - * Set the Hono types for Variables and Bindings. - * @returns The service instance with propper types - */ - setHonoTypes< - E extends { Bindings?: Record; Variables?: Record } = { - Bindings: {} - Variables: {} - }, - >() { - return this as unknown as HonoServiceClass - } - - /** - * Set a custom health function - * @param fn - */ - setHealthFunction(fn: HealthFunction) { - this.config.healthFunction = fn - return this - } - - /** - * Set the middleware which will be executed on all endpoints which are marked as secured/protected. - * It can also be used to enhance input information. - * - * @example - * ```typescript - * honoService.setProtectHandler(async function (c, next) { - * const auth = basicAuth({ username: 'user', password: 'password' }) - * c.set('additionalParameter', { userId: '123' }) - * return auth(c, next) - * }) - * ``` - * - * @param fn - */ - setProtectMiddleware(fn: EndpointProtectMiddleware) { - this.config.protectHandler = fn - return this - } - - async start() { - if (this.config.enableHealth) { - this.openApi.addPath(this.config.healthPath, { - get: { - summary: 'server health check', - description: - 'Returns a 200 response as long as given health function does not throw and the server is connected to the event bridge', - responses: { - '200': { - 'application/json': {}, - }, - }, - }, - }) - - const fn = this.config.healthFunction - - const healthFn = safeBind(fn, this) - - this.app.use('*', async (c, next) => { - if (!this.isAvailable) { - throw new HandledError(StatusCode.ServiceUnavailable, 'server not available') - } - - const traceId = c.req.header(this.config.traceHeaderField) - c.set('traceId', traceId) - await next() - if (traceId) { - c.header(this.config.traceHeaderField, traceId) - } - }) - - this.app.get(this.config.healthPath, async (c) => { - const con = propagation.extract(context.active(), c.req.raw.headers) - return await this.startActiveSpan('healthHandler', { kind: SpanKind.SERVER }, con, async (span) => { - span.setAttribute(SemanticAttributes.HTTP_ROUTE, this.config.healthPath) - span.setAttribute(SemanticAttributes.HTTP_METHOD, 'GET') - const isEventBridgeReady = await this.eventBridge.isHealthy() - - const traceId = c.req.header(this.config.traceHeaderField) - c.header(this.config.traceHeaderField, traceId) - - if (!isEventBridgeReady) { - const err = new HandledError(StatusCode.InternalServerError, 'event bridge not ready') - span.setAttribute(SemanticAttributes.HTTP_STATUS_CODE, err.errorCode) - return c.json(err.getErrorResponse(), StatusCode.InternalServerError) - } - - if (!this.isAvailable) { - const err = new HandledError(StatusCode.ServiceUnavailable, 'server not available') - span.setAttribute(SemanticAttributes.HTTP_STATUS_CODE, err.errorCode) - return c.json(err.getErrorResponse(), StatusCode.ServiceUnavailable) - } - - try { - await healthFn() - span.setStatus({ - code: SpanStatusCode.OK, - message: 'OK', - }) - const okErr = new HandledError(StatusCode.OK) - span.setAttribute(SemanticAttributes.HTTP_STATUS_CODE, okErr.errorCode) - return c.json(okErr.getErrorResponse(), okErr.errorCode as HonoStatusCode) - } catch (err) { - span.recordException(err as Error) - span.setStatus({ - code: SpanStatusCode.ERROR, - message: (err as Error).message, - }) - span.setAttribute(SemanticAttributes.HTTP_STATUS_CODE, StatusCode.InternalServerError) - return c.json(HandledError.fromError(err).getErrorResponse(), StatusCode.InternalServerError) - } - }) - }) - } - - if (this.config.openApi?.enabled) { - this.app.get(posix.join(this.config.apiMountPath, 'openapi.json'), async (c) => c.json(this.openApi.getSpec())) - - this.app.get(posix.join(this.config.apiMountPath, 'openapi.yaml'), async (c) => - c.text(this.openApi.getSpecAsYaml()), - ) - } - - this.app.notFound(async (c) => { - const con = propagation.extract(context.active(), c.req.raw.headers) - - return await this.startActiveSpan('notFoundHandler', { kind: SpanKind.SERVER }, con, async (span) => { - span.setAttribute(SemanticAttributes.HTTP_ROUTE, c.req.path) - span.setAttribute(SemanticAttributes.HTTP_METHOD, c.req.method.toUpperCase()) - span.setAttribute(SemanticAttributes.HTTP_STATUS_CODE, StatusCode.NotFound) - - const err = new HandledError(StatusCode.NotFound, 'Route not found', { - method: c.req.method, - route: c.req.url, - }) - span.recordException(err as Error) - span.setStatus({ - code: SpanStatusCode.ERROR, - message: (err as Error).message, - }) - - this.logger.debug({ path: c.req.path, ...span.spanContext(), customTraceId: c.get('traceId') }, 'not found') - return c.json(err.getErrorResponse(), StatusCode.NotFound) - }) - }) - - this.app.onError(async (err, c) => { - const con = propagation.extract(context.active(), c.req.raw.headers) - - return await this.startActiveSpan('errorHandler', { kind: SpanKind.SERVER }, con, async (span) => { - span.setAttribute(SemanticAttributes.HTTP_ROUTE, c.req.path) - span.setAttribute(SemanticAttributes.HTTP_METHOD, c.req.method.toUpperCase()) - span.recordException(err) - span.setStatus({ - code: SpanStatusCode.ERROR, - message: err.message, - }) - - if (err instanceof HandledError) { - span.setAttribute(SemanticAttributes.HTTP_STATUS_CODE, err.errorCode) - return c.json(err.getErrorResponse(), err.errorCode as HonoStatusCode) - } - - this.logger.error({ err, ...span.spanContext(), customTraceId: c.get('traceId') }, 'General error handler') - - if (err instanceof HTTPException) { - span.setAttribute(SemanticAttributes.HTTP_STATUS_CODE, err.status) - return c.json(HandledError.fromError(err, err.status as StatusCode).getErrorResponse(), err.status) - } - - span.setAttribute(SemanticAttributes.HTTP_STATUS_CODE, StatusCode.InternalServerError) - return c.json(new UnhandledError().getErrorResponse(), StatusCode.InternalServerError) - }) - }) - - this.registerService(...this.config.services) - - await this.setServiceAvailable() - - return super.start() - } - - /** - * Register a service instance. - * Must be called before `.start`. - * Adds the endpoints of the service commands to the Hono router - * @param services - */ - registerService(...services: Service[]) { - services.forEach((service) => { - service.commandDefinitionList.forEach((command) => { - this.addEndpoint(command.metadata, { ...service.serviceInfo, serviceTarget: command.commandName }) - }) - }) - - return this - } - - /** - * Adds a single service command endpoint to the Hono router - * @param metadata - * @param commandName - * @param service - * @returns - */ - public addEndpoint(metadata: CommandDefinitionMetadataBase, service: EBMessageAddress) { - if (!isHttpExposedServiceMeta(metadata)) { - return - } - - if (this.knownServices.has(`${service.serviceName}-${service.serviceVersion}-${service.serviceTarget}`)) { - return - } - - const expose = metadata.expose - - const method = expose.http.method.toLowerCase() as 'put' | 'post' | 'patch' | 'get' | 'delete' - const path = posix.join(this.config.apiMountPath, `v${service.serviceVersion}`, expose.http.path) - - const requestContentType = expose.contentTypeRequest ?? 'application/json' - const requestEncodingType = expose.contentEncodingRequest ?? 'utf-8' - - const responseContentType = expose.contentTypeResponse ?? 'application/json' - const responseEncodingType = expose.contentEncodingResponse ?? 'utf-8' - - const protectHandler = safeBind(this.config.protectHandler, this) - - addPathToOpenApi(this.openApi, metadata, path, this.config) - - const handler: Handler = async (c) => { - const parentContext = propagation.extract(context.active(), c.req.raw.headers) - - return this.startActiveSpan('handler', { kind: SpanKind.SERVER }, parentContext, async (span) => { - try { - span.setAttribute(SemanticAttributes.HTTP_ROUTE, path) - span.setAttribute(SemanticAttributes.HTTP_METHOD, method.toUpperCase()) - - let payload: unknown - - const parameter = { - ...c.req.query(), - ...c.req.param(), - ...c.get('additionalParameter'), - } - - if (method !== 'get' && method !== 'delete') { - const contentType = c.req.header('content-type')?.toLowerCase() - - if (!contentType?.includes(requestContentType)) { - throw new HandledError( - StatusCode.BadRequest, - `Request must be content type ${requestContentType} ${requestEncodingType}`, - ) - } - - try { - if (contentType?.includes('application/json')) { - payload = await c.req.json() - } else if ( - contentType?.includes('multipart/form-data') || - contentType?.includes('application/x-www-form-urlencoded') - ) { - payload = await c.req.parseBody() - } else { - payload = await c.req.text() - } - } catch (error) { - const err = HandledError.fromError(error, StatusCode.BadRequest) - this.logger.error({ err, contentType, path: c.req.path, method }, 'Failed to decode body') - return c.json(err.getErrorResponse(), err.errorCode as HonoStatusCode) - } - } - - const traceId = c.get('traceId') || c.req.header(this.config.traceHeaderField) - - const result = await this.invoke( - { - traceId, - receiver: service, - payload: { - payload, - parameter, - }, - principalId: c.get('principalId'), - tenantId: c.get('tenantId'), - contentType: expose.contentTypeRequest ?? 'application/json', - contentEncoding: expose.contentEncodingRequest ?? 'utf-8', - }, - `${method}:${path}`, - ) - - c.header('content-type', `${responseContentType}; charset=${responseEncodingType}`) - - span.setStatus({ - code: SpanStatusCode.OK, - }) - - if (result === undefined || result === null || result === '') { - span.setAttribute(SemanticAttributes.HTTP_STATUS_CODE, StatusCode.NoContent) - if (responseContentType.toLowerCase() !== 'application/json') { - return c.text('', StatusCode.NoContent) - } - return c.json(undefined, StatusCode.NoContent) - } - - span.setAttribute(SemanticAttributes.HTTP_STATUS_CODE, StatusCode.OK) - - if (responseContentType.toLowerCase() !== 'application/json') { - return c.text(result.toString(), StatusCode.OK) - } - - return c.json(result, StatusCode.OK) - } catch (err) { - span.recordException(err as Error) - span.setStatus({ - code: SpanStatusCode.ERROR, - message: (err as Error).message, - }) - - if (err instanceof HandledError) { - this.logger.debug({ err, ...span.spanContext(), customTraceId: c.get('traceId') }, err.message) - - span.setAttribute(SemanticAttributes.HTTP_STATUS_CODE, err.errorCode) - return c.json(err.getErrorResponse(), err.errorCode as HonoStatusCode) - } - - const unhandledError = UnhandledError.fromError(err) - unhandledError.errorCode = StatusCode.InternalServerError - span.setAttribute(SemanticAttributes.HTTP_STATUS_CODE, unhandledError.errorCode) - - this.logger.error({ err, ...span.spanContext(), customTraceId: c.get('traceId') }, 'unhandled error') - return c.json(unhandledError.getErrorResponse(), unhandledError.errorCode) - } - }) - } - - if (expose.http.openApi?.isSecure && this.config.protectHandler) { - this.app[method](path, protectHandler, handler) - } else { - this.app[method](path, handler) - } - this.knownServices.add(`${service.serviceName}-${service.serviceVersion}-${service.serviceTarget}`) - } - - async invoke( - input: Omit, - endpoint: string, - ): Promise { - return this.eventBridge.invoke({ - sender: { - serviceName: this.serviceInfo.serviceName, - serviceVersion: this.serviceInfo.serviceVersion, - serviceTarget: `$$endpoint:${endpoint}`, - instanceId: this.eventBridge.instanceId, - }, - ...input, - }) - } - - /** - * Set the service unavailable - * The webserver will return 503 Service Unavailable - */ - async setServiceUnavailable() { - this.isAvailable = false - } - - /** - * Set the service available - * Request will be processed. - */ - async setServiceAvailable() { - this.isAvailable = true - } - - /** - * Helper function to be used in gracefulShutdown. - * It prevents to handle new requests during shut down. - * Incoming requests are rejected with 503 Service Unavailable. - * - * @example - * ```typescript - * gracefulShutdown(logger, [ - * honoService.prepareDestroy(), - * eventbridge, - * ...services, - * honoService - * ]) - * ``` - * @returns - */ - prepareDestroy() { - return { - name: `${this.serviceInfo.serviceName} ${this.serviceInfo.serviceVersion} prepare shutdown`, - destroy: this.setServiceUnavailable, - } - } - - async destroy() { - await this.setServiceUnavailable() - return super.destroy() - } + Bindings extends BindingsBase = BindingsBase, + Variables extends VariablesBase = VariablesBase, +> extends Service> { + /** + * The Hono instance + */ + public app + + /** + * The OpenApiBuilder instance + */ + public openApi: OpenApiBuilder + + private knownServices: Set = new Set() + + private isAvailable = false + + constructor(config: ServiceConstructorInput>) { + super(config) + this.openApi = new OpenApiBuilder(this.config.openApi) + + if (this.config.enableDynamicRoutes) { + this.app = new Hono<{ Bindings: Bindings; Variables: Variables }>({ router: new PatternRouter() }) + } else { + this.app = new Hono<{ Bindings: Bindings; Variables: Variables }>() + this.subscriptionDefinitionList = [] + } + } + + /** + * Set the Hono types for Variables and Bindings. + * @returns The service instance with propper types + */ + setHonoTypes< + E extends { Bindings?: Record; Variables?: Record } = { + Bindings: EmptyObject + Variables: EmptyObject + }, + >() { + return this as unknown as HonoServiceClass + } + + /** + * Set a custom health function + * @param fn + */ + setHealthFunction(fn: HealthFunction) { + this.config.healthFunction = fn + return this + } + + /** + * Set the middleware which will be executed on all endpoints which are marked as secured/protected. + * It can also be used to enhance input information. + * + * @example + * ```typescript + * honoService.setProtectHandler(async function (c, next) { + * const auth = basicAuth({ username: 'user', password: 'password' }) + * c.set('additionalParameter', { userId: '123' }) + * return auth(c, next) + * }) + * ``` + * + * @param fn + */ + setProtectMiddleware(fn: EndpointProtectMiddleware) { + this.config.protectHandler = fn + return this + } + + async start() { + if (this.config.enableHealth) { + this.openApi.addPath(this.config.healthPath, { + get: { + summary: 'server health check', + description: + 'Returns a 200 response as long as given health function does not throw and the server is connected to the event bridge', + responses: { + '200': { + 'application/json': {}, + }, + }, + }, + }) + + const fn = this.config.healthFunction + + const healthFn = safeBind(fn, this) + + this.app.use('*', async (c, next) => { + if (!this.isAvailable) { + throw new HandledError(StatusCode.ServiceUnavailable, 'server not available') + } + + const traceId = c.req.header(this.config.traceHeaderField) + c.set('traceId', traceId) + await next() + if (traceId) { + c.header(this.config.traceHeaderField, traceId) + } + }) + + this.app.get(this.config.healthPath, async c => { + const con = propagation.extract(context.active(), c.req.raw.headers) + return await this.startActiveSpan('healthHandler', { kind: SpanKind.SERVER }, con, async span => { + span.setAttribute(ATTR_HTTP_ROUTE, this.config.healthPath) + span.setAttribute(ATTR_HTTP_METHOD, 'GET') + const isEventBridgeReady = await this.eventBridge.isHealthy() + + const traceId = c.req.header(this.config.traceHeaderField) + c.header(this.config.traceHeaderField, traceId) + + if (!isEventBridgeReady) { + const err = new HandledError(StatusCode.InternalServerError, 'event bridge not ready') + span.setAttribute(ATTR_HTTP_STATUS_CODE, err.errorCode) + return c.json(err.getErrorResponse(), StatusCode.InternalServerError) + } + + if (!this.isAvailable) { + const err = new HandledError(StatusCode.ServiceUnavailable, 'server not available') + span.setAttribute(ATTR_HTTP_STATUS_CODE, err.errorCode) + return c.json(err.getErrorResponse(), StatusCode.ServiceUnavailable) + } + + try { + await healthFn() + span.setStatus({ + code: SpanStatusCode.OK, + message: 'OK', + }) + const okErr = new HandledError(StatusCode.OK) + span.setAttribute(ATTR_HTTP_STATUS_CODE, okErr.errorCode) + return c.json(okErr.getErrorResponse(), okErr.errorCode as ContentfulStatusCode) + } catch (err) { + span.recordException(err as Error) + span.setStatus({ + code: SpanStatusCode.ERROR, + message: (err as Error).message, + }) + span.setAttribute(ATTR_HTTP_STATUS_CODE, StatusCode.InternalServerError) + return c.json(HandledError.fromError(err).getErrorResponse(), StatusCode.InternalServerError) + } + }) + }) + } + + if (this.config.openApi?.enabled) { + this.app.get(posix.join(this.config.apiMountPath, 'openapi.json'), async c => c.json(this.openApi.getSpec())) + + this.app.get(posix.join(this.config.apiMountPath, 'openapi.yaml'), async c => + c.text(this.openApi.getSpecAsYaml()), + ) + } + + this.app.notFound(async c => { + const con = propagation.extract(context.active(), c.req.raw.headers) + + return await this.startActiveSpan('notFoundHandler', { kind: SpanKind.SERVER }, con, async span => { + span.setAttribute(ATTR_HTTP_ROUTE, c.req.path) + span.setAttribute(ATTR_HTTP_METHOD, c.req.method.toUpperCase()) + span.setAttribute(ATTR_HTTP_STATUS_CODE, StatusCode.NotFound) + + const err = new HandledError(StatusCode.NotFound, 'Route not found', { + method: c.req.method, + route: c.req.url, + }) + span.recordException(err as Error) + span.setStatus({ + code: SpanStatusCode.ERROR, + message: (err as Error).message, + }) + + this.logger.debug({ path: c.req.path, ...span.spanContext(), customTraceId: c.get('traceId') }, 'not found') + return c.json(err.getErrorResponse(), StatusCode.NotFound) + }) + }) + + this.app.onError(async (err, c) => { + const con = propagation.extract(context.active(), c.req.raw.headers) + + return await this.startActiveSpan('errorHandler', { kind: SpanKind.SERVER }, con, async span => { + span.setAttribute(ATTR_HTTP_ROUTE, c.req.path) + span.setAttribute(ATTR_HTTP_METHOD, c.req.method.toUpperCase()) + span.recordException(err) + span.setStatus({ + code: SpanStatusCode.ERROR, + message: err.message, + }) + + if (err instanceof HandledError) { + span.setAttribute(ATTR_HTTP_STATUS_CODE, err.errorCode) + return c.json(err.getErrorResponse(), err.errorCode as ContentfulStatusCode) + } + + this.logger.error({ err, ...span.spanContext(), customTraceId: c.get('traceId') }, 'General error handler') + + if (err instanceof HTTPException) { + span.setAttribute(ATTR_HTTP_STATUS_CODE, err.status) + return c.json(HandledError.fromError(err, err.status as StatusCode).getErrorResponse(), err.status) + } + + span.setAttribute(ATTR_HTTP_STATUS_CODE, StatusCode.InternalServerError) + return c.json(new UnhandledError().getErrorResponse(), StatusCode.InternalServerError) + }) + }) + + this.registerService(...this.config.services) + + await this.setServiceAvailable() + + return super.start() + } + + /** + * Register a service instance. + * Must be called before `.start`. + * Adds the endpoints of the service commands to the Hono router + * @param services + */ + registerService(...services: Service[]) { + for (const service of services) { + for (const command of service.commandDefinitionList) { + this.addEndpoint(command.metadata, { ...service.serviceInfo, serviceTarget: command.commandName }) + } + } + + return this + } + + /** + * Adds a single service command endpoint to the Hono router + * @param metadata + * @param commandName + * @param service + * @returns + */ + public addEndpoint(metadata: CommandDefinitionMetadataBase, service: EBMessageAddress) { + if (!isHttpExposedServiceMeta(metadata)) { + return + } + + if (this.knownServices.has(`${service.serviceName}-${service.serviceVersion}-${service.serviceTarget}`)) { + return + } + + const expose = metadata.expose + + const method = expose.http.method.toLowerCase() as 'put' | 'post' | 'patch' | 'get' | 'delete' + const path = posix.join(this.config.apiMountPath, `v${service.serviceVersion}`, expose.http.path) + + const requestContentType = expose.contentTypeRequest ?? 'application/json' + const requestEncodingType = expose.contentEncodingRequest ?? 'utf-8' + + const responseContentType = expose.contentTypeResponse ?? 'application/json' + const responseEncodingType = expose.contentEncodingResponse ?? 'utf-8' + + const protectHandler = safeBind(this.config.protectHandler, this) + + addPathToOpenApi(this.openApi, metadata, path, this.config) + + const handler: Handler = async c => { + const parentContext = propagation.extract(context.active(), c.req.raw.headers) + + return this.startActiveSpan('handler', { kind: SpanKind.SERVER }, parentContext, async span => { + try { + span.setAttribute(ATTR_HTTP_ROUTE, path) + span.setAttribute(ATTR_HTTP_METHOD, method.toUpperCase()) + + let payload: unknown + + const parameter = { + ...c.req.query(), + ...c.req.param(), + ...c.get('additionalParameter'), + } + + if (method !== 'get' && method !== 'delete') { + const contentType = c.req.header('content-type')?.toLowerCase() + + if (!contentType?.includes(requestContentType)) { + throw new HandledError( + StatusCode.BadRequest, + `Request must be content type ${requestContentType} ${requestEncodingType}`, + ) + } + + try { + if (contentType?.includes('application/json')) { + payload = await c.req.json() + } else if ( + contentType?.includes('multipart/form-data') || + contentType?.includes('application/x-www-form-urlencoded') + ) { + payload = await c.req.parseBody() + } else { + payload = await c.req.text() + } + } catch (error) { + const err = HandledError.fromError(error, StatusCode.BadRequest) + this.logger.error({ err, contentType, path: c.req.path, method }, 'Failed to decode body') + return c.json(err.getErrorResponse(), err.errorCode as ContentfulStatusCode) + } + } + + const traceId = c.get('traceId') || c.req.header(this.config.traceHeaderField) + + const result = await this.invoke( + { + traceId, + receiver: service, + payload: { + payload, + parameter, + }, + principalId: c.get('principalId'), + tenantId: c.get('tenantId'), + contentType: expose.contentTypeRequest ?? 'application/json', + contentEncoding: expose.contentEncodingRequest ?? 'utf-8', + }, + `${method}:${path}`, + ) + + c.header('content-type', `${responseContentType}; charset=${responseEncodingType}`) + + span.setStatus({ + code: SpanStatusCode.OK, + }) + + if (result === undefined || result === null || result === '') { + span.setAttribute(ATTR_HTTP_STATUS_CODE, StatusCode.NoContent) + if (responseContentType.toLowerCase() !== 'application/json') { + return c.body(null, StatusCode.NoContent) + } + return c.body(null, StatusCode.NoContent) + } + + span.setAttribute(ATTR_HTTP_STATUS_CODE, StatusCode.OK) + + if (responseContentType.toLowerCase() !== 'application/json') { + return c.text(result.toString(), StatusCode.OK) + } + + return c.json(result, StatusCode.OK) + } catch (err) { + span.recordException(err as Error) + span.setStatus({ + code: SpanStatusCode.ERROR, + message: (err as Error).message, + }) + + if (err instanceof HandledError) { + this.logger.debug({ err, ...span.spanContext(), customTraceId: c.get('traceId') }, err.message) + + span.setAttribute(ATTR_HTTP_STATUS_CODE, err.errorCode) + return c.json(err.getErrorResponse(), err.errorCode as ContentfulStatusCode) + } + + const unhandledError = UnhandledError.fromError(err) + unhandledError.errorCode = StatusCode.InternalServerError + span.setAttribute(ATTR_HTTP_STATUS_CODE, unhandledError.errorCode) + + this.logger.error({ err, ...span.spanContext(), customTraceId: c.get('traceId') }, 'unhandled error') + return c.json(unhandledError.getErrorResponse(), unhandledError.errorCode) + } + }) + } + + if (expose.http.openApi?.isSecure && this.config.protectHandler) { + this.app[method](path, protectHandler, handler) + } else { + this.app[method](path, handler) + } + this.knownServices.add(`${service.serviceName}-${service.serviceVersion}-${service.serviceTarget}`) + } + + async invoke( + input: Omit, + endpoint: string, + ): Promise { + return this.eventBridge.invoke({ + sender: { + serviceName: this.serviceInfo.serviceName, + serviceVersion: this.serviceInfo.serviceVersion, + serviceTarget: `$$endpoint:${endpoint}`, + instanceId: this.eventBridge.instanceId, + }, + ...input, + }) + } + + /** + * Set the service unavailable + * The webserver will return 503 Service Unavailable + */ + async setServiceUnavailable() { + this.isAvailable = false + } + + /** + * Set the service available + * Request will be processed. + */ + async setServiceAvailable() { + this.isAvailable = true + } + + /** + * Helper function to be used in gracefulShutdown. + * It prevents to handle new requests during shut down. + * Incoming requests are rejected with 503 Service Unavailable. + * + * @example + * ```typescript + * gracefulShutdown(logger, [ + * honoService.prepareDestroy(), + * eventbridge, + * ...services, + * honoService + * ]) + * ``` + * @returns + */ + prepareDestroy() { + return { + name: `${this.serviceInfo.serviceName} ${this.serviceInfo.serviceVersion} prepare shutdown`, + destroy: this.setServiceUnavailable, + } + } + + async destroy() { + await this.setServiceUnavailable() + return super.destroy() + } } diff --git a/packages/hono-http-server/src/service/hono/v1/honoServiceConfig.ts b/packages/hono-http-server/src/service/hono/v1/honoServiceConfig.ts index f67b5b42d..94ef2634b 100644 --- a/packages/hono-http-server/src/service/hono/v1/honoServiceConfig.ts +++ b/packages/hono-http-server/src/service/hono/v1/honoServiceConfig.ts @@ -6,70 +6,72 @@ import { z } from 'zod' export const DEFAULT_API_MOUNT_PATH = '/api' export const OPENAPI_DEFAULT_INFO = { - title: 'Server api', - description: 'OpenApi definition for server endpoints', - version: '1.0.0', + title: 'Server api', + description: 'OpenApi definition for server endpoints', + version: '1.0.0', } export const ExternalDocumentationObjectSchema = z.object({ - description: z.string().optional(), - url: z.string().url(), + description: z.string().optional(), + url: z.string().url(), }) export const TagObjectSchema = z.object({ - name: z.string(), - description: z.string().optional(), - externalDocs: ExternalDocumentationObjectSchema.optional(), + name: z.string(), + description: z.string().optional(), + externalDocs: ExternalDocumentationObjectSchema.optional(), }) export const InfoObjectSchema = z.object({ - title: z.string().default('PURISTA'), - description: z.string().default('OpenApi definition for server endpoints'), - termsOfService: z.string().optional(), - contact: z - .object({ - name: z.string().optional(), - url: z.string().optional(), - email: z.string().optional(), - }) - .optional(), - license: z - .object({ - name: z.string(), - url: z.string().optional(), - }) - .optional(), - version: z.string().default('1.0.0'), + title: z.string().default('PURISTA'), + description: z.string().default('OpenApi definition for server endpoints'), + termsOfService: z.string().optional(), + contact: z + .object({ + name: z.string().optional(), + url: z.string().optional(), + email: z.string().optional(), + }) + .optional(), + license: z + .object({ + name: z.string(), + url: z.string().optional(), + }) + .optional(), + version: z.string().default('1.0.0'), }) export const ServerObjectSchema = z.object({ - url: z.string(), - description: z.string().optional(), - variables: z.any().optional(), + url: z.string(), + description: z.string().optional(), + variables: z.any().optional(), }) export const honoServiceV1ConfigSchema = z.object({ - logLevel: z.enum(['info', 'error', 'warn', 'debug', 'trace', 'fatal']).optional(), - enableDynamicRoutes: z.boolean().default(false), - apiMountPath: z.string().optional().default(DEFAULT_API_MOUNT_PATH), - enableHealth: z.boolean().optional().default(true), - healthPath: z.string().optional().default('/healthz'), - healthFunction: z.any().default(function () {}), - protectHandler: z.any().default(function () {}), - services: z.array(z.instanceof(Service)).optional().default([]), - traceHeaderField: z.string().default('x-trace-id'), - openApi: z - .object({ - openapi: z.string().default('3.1.0'), - enabled: z.boolean().optional().default(true), - info: InfoObjectSchema, - servers: z.array(ServerObjectSchema).optional(), - components: z.any().optional(), - security: z.array(z.any()).optional(), - externalDocs: ExternalDocumentationObjectSchema.optional(), - tags: z.array(TagObjectSchema).optional(), - paths: z.record(z.string(), z.record(z.string(), z.any())).optional(), - }) - .optional(), + logLevel: z.enum(['info', 'error', 'warn', 'debug', 'trace', 'fatal']).optional().default('warn'), + enableDynamicRoutes: z.boolean().default(false), + apiMountPath: z.string().optional().default(DEFAULT_API_MOUNT_PATH), + enableHealth: z.boolean().optional().default(true), + healthPath: z.string().optional().default('/healthz'), + healthFunction: z.any().default(function () {}), + protectHandler: z.any().default(async function (_c: any, next: any) { + await next() + }), + services: z.array(z.instanceof(Service)).optional().default([]), + traceHeaderField: z.string().default('x-trace-id'), + openApi: z + .object({ + openapi: z.string().default('3.1.0'), + enabled: z.boolean().optional().default(true), + info: InfoObjectSchema, + servers: z.array(ServerObjectSchema).optional(), + components: z.any().optional(), + security: z.array(z.any()).optional(), + externalDocs: ExternalDocumentationObjectSchema.optional(), + tags: z.array(TagObjectSchema).optional(), + paths: z.record(z.string(), z.record(z.string(), z.any())).optional(), + }) + .optional(), }) export type HonoServiceV1Config = z.output diff --git a/packages/hono-http-server/src/service/hono/v1/honoV1Service.test.ts b/packages/hono-http-server/src/service/hono/v1/honoV1Service.test.ts index 3cba582e6..98e04ac85 100644 --- a/packages/hono-http-server/src/service/hono/v1/honoV1Service.test.ts +++ b/packages/hono-http-server/src/service/hono/v1/honoV1Service.test.ts @@ -1,11 +1,7 @@ import { honoV1Service as service } from './honoV1Service.js' describe('service hono version 1', () => { - it('has valid commands', () => { - service.validateCommandDefinitions() - }) - - it('has valid subscriptions', () => { - service.validateSubscriptionDefinitions() - }) + it('has valid service setup', () => { + service.testServiceSetup() + }) }) diff --git a/packages/hono-http-server/src/service/hono/v1/honoV1Service.ts b/packages/hono-http-server/src/service/hono/v1/honoV1Service.ts index a30b0aa26..1f2a74bd4 100644 --- a/packages/hono-http-server/src/service/hono/v1/honoV1Service.ts +++ b/packages/hono-http-server/src/service/hono/v1/honoV1Service.ts @@ -10,9 +10,9 @@ import { serviceCommandsToRestApiSubscriptionBuilder } from './subscription/serv const commandDefinitions: CommandDefinitionList = [] const subscriptionDefinitions: SubscriptionDefinitionList = [ - serviceCommandsToRestApiSubscriptionBuilder.getDefinition(), + serviceCommandsToRestApiSubscriptionBuilder.getDefinition(), ] export const honoV1Service = honoV1ServiceBuilder - .addCommandDefinition(...commandDefinitions) - .addSubscriptionDefinition(...subscriptionDefinitions) + .addCommandDefinition(...commandDefinitions) + .addSubscriptionDefinition(...subscriptionDefinitions) diff --git a/packages/hono-http-server/src/service/hono/v1/honoV1ServiceBuilder.ts b/packages/hono-http-server/src/service/hono/v1/honoV1ServiceBuilder.ts index 8c81ed274..b660ecee4 100644 --- a/packages/hono-http-server/src/service/hono/v1/honoV1ServiceBuilder.ts +++ b/packages/hono-http-server/src/service/hono/v1/honoV1ServiceBuilder.ts @@ -3,32 +3,15 @@ import { ServiceBuilder } from '@purista/core' import { generalHonoServiceInfo } from '../generalHonoServiceInfo.js' import { HonoServiceClass } from './HonoServiceClass.js' -import { DEFAULT_API_MOUNT_PATH, honoServiceV1ConfigSchema, OPENAPI_DEFAULT_INFO } from './honoServiceConfig.js' +import { honoServiceV1ConfigSchema } from './honoServiceConfig.js' export const honoServiceInfo: ServiceInfoType = { - serviceVersion: '1', - ...generalHonoServiceInfo, + serviceVersion: '1', + ...generalHonoServiceInfo, } // create a service builder instance and assign service config schema and default config. export const honoV1ServiceBuilder = new ServiceBuilder(honoServiceInfo) - .setConfigSchema(honoServiceV1ConfigSchema) - .setDefaultConfig({ - logLevel: 'warn', - enableDynamicRoutes: false, - apiMountPath: DEFAULT_API_MOUNT_PATH, - enableHealth: true, - healthPath: '/healthz', - healthFunction: function () {}, - protectHandler: function () {}, - services: [], - traceHeaderField: 'x-trace-id', - openApi: { - openapi: '3.1.0', - enabled: true, - info: OPENAPI_DEFAULT_INFO, - paths: undefined, - }, - }) - .setCustomClass(HonoServiceClass) + .setConfigSchema(honoServiceV1ConfigSchema) + .setCustomClass(HonoServiceClass) diff --git a/packages/hono-http-server/src/service/hono/v1/subscription/serviceCommandsToRestApi/serviceCommandsToRestApi.test.ts b/packages/hono-http-server/src/service/hono/v1/subscription/serviceCommandsToRestApi/serviceCommandsToRestApi.test.ts index a955319c1..22d319e60 100644 --- a/packages/hono-http-server/src/service/hono/v1/subscription/serviceCommandsToRestApi/serviceCommandsToRestApi.test.ts +++ b/packages/hono-http-server/src/service/hono/v1/subscription/serviceCommandsToRestApi/serviceCommandsToRestApi.test.ts @@ -1,10 +1,4 @@ -import { - getCommandSuccessMessageMock, - getEventBridgeMock, - getLoggerMock, - getSubscriptionContextMock, - safeBind, -} from '@purista/core' +import { getCommandSuccessMessageMock, getEventBridgeMock, getLoggerMock, safeBind } from '@purista/core' import { createSandbox } from 'sinon' import { honoV1Service } from '../../honoV1Service.js' @@ -12,42 +6,43 @@ import { serviceCommandsToRestApiSubscriptionBuilder } from './serviceCommandsTo import type { HonoV1ServiceCommandsToRestApiInputPayload } from './types.js' describe('service Hono version 1 - subscription serviceCommandsToRestApi', () => { - let sandbox = createSandbox() - beforeEach(() => { - sandbox = createSandbox() - }) + let sandbox = createSandbox() + beforeEach(() => { + sandbox = createSandbox() + }) - afterEach(() => { - sandbox.restore() - }) + afterEach(() => { + sandbox.restore() + }) - test('does not throw', async () => { - // create a service instance to be bind to the subscription function - const service = await honoV1Service.getInstance(getEventBridgeMock(sandbox).mock, { - logger: getLoggerMock(sandbox).mock, - }) + test('does not throw', async () => { + // create a service instance to be bind to the subscription function + const service = await honoV1Service.getInstance(getEventBridgeMock(sandbox).mock, { + logger: getLoggerMock(sandbox).mock, + serviceConfig: {}, + }) - // get the subscription function and bind to service instance to work properly - const serviceCommandsToRestApi = safeBind( - serviceCommandsToRestApiSubscriptionBuilder.getSubscriptionFunction(), - service, - ) + // get the subscription function and bind to service instance to work properly + const serviceCommandsToRestApi = safeBind( + serviceCommandsToRestApiSubscriptionBuilder.getSubscriptionFunction(), + service, + ) - // define the test input payload - const payload: HonoV1ServiceCommandsToRestApiInputPayload = undefined + // define the test input payload + const payload: HonoV1ServiceCommandsToRestApiInputPayload = undefined - // define the test input parameter - const parameter = undefined as unknown as Readonly + // define the test input parameter + const parameter = undefined as unknown as Readonly - // create a mock message with the expected input for the subscription function - const message = getCommandSuccessMessageMock(payload) + // create a mock message with the expected input for the subscription function + const message = getCommandSuccessMessageMock(payload) - // create a subscription context for the subscription function - const context = getSubscriptionContextMock(message, sandbox) + // create a subscription context for the subscription function + const context = serviceCommandsToRestApiSubscriptionBuilder.getSubscriptionContextMock({ message, sandbox }) - // execute the subscription function - const result = await serviceCommandsToRestApi(context.mock, payload, parameter) + // execute the subscription function + const result = await serviceCommandsToRestApi(context.mock, payload, parameter) - expect(result).toBeUndefined() - }) + expect(result).toBeUndefined() + }) }) diff --git a/packages/hono-http-server/src/service/hono/v1/subscription/serviceCommandsToRestApi/serviceCommandsToRestApiSubscriptionBuilder.ts b/packages/hono-http-server/src/service/hono/v1/subscription/serviceCommandsToRestApi/serviceCommandsToRestApiSubscriptionBuilder.ts index 8acf555b8..a740478ca 100644 --- a/packages/hono-http-server/src/service/hono/v1/subscription/serviceCommandsToRestApi/serviceCommandsToRestApiSubscriptionBuilder.ts +++ b/packages/hono-http-server/src/service/hono/v1/subscription/serviceCommandsToRestApi/serviceCommandsToRestApiSubscriptionBuilder.ts @@ -4,19 +4,19 @@ import { honoV1ServiceBuilder } from '../../honoV1ServiceBuilder.js' import { honoV1ServiceCommandsToRestApiInputPayloadSchema } from './schema.js' export const serviceCommandsToRestApiSubscriptionBuilder = honoV1ServiceBuilder - .getSubscriptionBuilder( - 'serviceCommandsToRestApi', - 'listens for InfoMessages and adds endpoints for commands if they are configured to be exposed as http endpoint', - ) - .adviceDurable(false) - .adviceAutoacknowledgeMessage() - .filterForMessageType(EBMessageType.InfoServiceFunctionAdded) - .receiveMessageOnEveryInstance() - .addPayloadSchema(honoV1ServiceCommandsToRestApiInputPayloadSchema) - .setSubscriptionFunction(async function ({ message }, payload, _parameter) { - if (!isHttpExposedServiceMeta(payload)) { - return - } + .getSubscriptionBuilder( + 'serviceCommandsToRestApi', + 'listens for InfoMessages and adds endpoints for commands if they are configured to be exposed as http endpoint', + ) + .adviceDurable(false) + .adviceAutoacknowledgeMessage() + .filterForMessageType(EBMessageType.InfoServiceFunctionAdded) + .receiveMessageOnEveryInstance() + .addPayloadSchema(honoV1ServiceCommandsToRestApiInputPayloadSchema) + .setSubscriptionFunction(async function ({ message }, payload, _parameter) { + if (!isHttpExposedServiceMeta(payload)) { + return + } - this.addEndpoint(payload, message.sender) - }) + this.addEndpoint(payload, message.sender) + }) diff --git a/packages/hono-http-server/src/service/hono/v1/subscription/serviceCommandsToRestApi/types.ts b/packages/hono-http-server/src/service/hono/v1/subscription/serviceCommandsToRestApi/types.ts index d29af8378..9bfd078b1 100644 --- a/packages/hono-http-server/src/service/hono/v1/subscription/serviceCommandsToRestApi/types.ts +++ b/packages/hono-http-server/src/service/hono/v1/subscription/serviceCommandsToRestApi/types.ts @@ -3,5 +3,5 @@ import type { z } from 'zod' import type { honoV1ServiceCommandsToRestApiInputPayloadSchema } from './schema.js' export type HonoV1ServiceCommandsToRestApiInputPayload = z.output< - typeof honoV1ServiceCommandsToRestApiInputPayloadSchema + typeof honoV1ServiceCommandsToRestApiInputPayloadSchema > diff --git a/packages/hono-http-server/src/types/BindingsBase.ts b/packages/hono-http-server/src/types/BindingsBase.ts index 0c664a2f3..1ce2f44df 100644 --- a/packages/hono-http-server/src/types/BindingsBase.ts +++ b/packages/hono-http-server/src/types/BindingsBase.ts @@ -1 +1,3 @@ -export type BindingsBase = {} +import type { EmptyObject } from '@purista/core' + +export type BindingsBase = EmptyObject diff --git a/packages/hono-http-server/src/types/EndpointProtectMiddleware.ts b/packages/hono-http-server/src/types/EndpointProtectMiddleware.ts index e49c3cd78..9849cf31c 100644 --- a/packages/hono-http-server/src/types/EndpointProtectMiddleware.ts +++ b/packages/hono-http-server/src/types/EndpointProtectMiddleware.ts @@ -5,7 +5,8 @@ import type { BindingsBase } from './BindingsBase.js' import type { VariablesBase } from './VariablesBase.js' export type EndpointProtectMiddleware< - T extends Service, - Bindings extends BindingsBase = BindingsBase, - Variables extends VariablesBase = VariablesBase, + T extends Service, + Bindings extends BindingsBase = BindingsBase, + Variables extends VariablesBase = VariablesBase, + // biome-ignore lint/suspicious/noConfusingVoidType: > = (this: T, c: Context<{ Bindings: Bindings; Variables: Variables }>, next: Next) => Promise diff --git a/packages/hono-http-server/src/types/VariablesBase.ts b/packages/hono-http-server/src/types/VariablesBase.ts index 067bc99c2..483f529f3 100644 --- a/packages/hono-http-server/src/types/VariablesBase.ts +++ b/packages/hono-http-server/src/types/VariablesBase.ts @@ -1,14 +1,14 @@ export type VariablesBase = { - /** - * Additional parameter passed to the commands - */ - additionalParameter?: Record - /** The principal ID */ - principalId?: string - /** The tenant ID */ - tenantId?: string - /** The custom trace ID */ - traceId?: string - /** The instance ID */ - instanceId?: string + /** + * Additional parameter passed to the commands + */ + additionalParameter?: Record + /** The principal ID */ + principalId?: string + /** The tenant ID */ + tenantId?: string + /** The custom trace ID */ + traceId?: string + /** The instance ID */ + instanceId?: string } diff --git a/packages/hono-http-server/test/integration.test.ts b/packages/hono-http-server/test/integration.test.ts index 0592850a9..8e16afda3 100644 --- a/packages/hono-http-server/test/integration.test.ts +++ b/packages/hono-http-server/test/integration.test.ts @@ -1,7 +1,7 @@ import { serve } from '@hono/node-server' import { swaggerUI } from '@hono/swagger-ui' import type { EventBridge } from '@purista/core' -import { DefaultEventBridge, getLoggerMock, HttpClient } from '@purista/core' +import { DefaultEventBridge, HttpClient, getLoggerMock } from '@purista/core' import type { OpenAPIObject } from 'openapi3-ts/oas31' import type { HonoServiceClass } from '../src/service/hono/v1/HonoServiceClass.js' @@ -9,135 +9,135 @@ import { honoV1Service } from '../src/service/hono/v1/honoV1Service.js' import { theServiceV1Service } from './service/theService/v1/index.js' describe('httpserver integration test', () => { - let eventBridge: EventBridge - let server: HonoServiceClass - let serverInstance: ReturnType - const port = 3000 - const client = new HttpClient({ - logger: getLoggerMock().mock, - baseUrl: `http://127.0.0.1:${port}`, - defaultHeaders: { 'content-type': 'application/json; charset=utf-8' }, - }) - - const content = { some: 'content' } - - const apiMountPath = '/api' - - const config = { - logger: getLoggerMock().mock, - serviceConfig: { - enableDynamicRoutes: true, - enableHealthz: true, - apiMountPath, - openApi: { - enabled: true, - info: { - title: 'backend api', - description: 'OpenApi definition for server endpoints', - version: '1.0.0', - }, - }, - }, - } - - beforeAll(async () => { - eventBridge = new DefaultEventBridge({ logger: getLoggerMock().mock }) - await eventBridge.start() - - server = await honoV1Service.getInstance(eventBridge, config) - server.app.get('/api', swaggerUI({ url: '/api/openapi.json' })) - server.setProtectMiddleware(async function (_c, next) { - await next() - }) - await server.start() - - serverInstance = serve({ - fetch: server.app.fetch, - port, - }) - - await new Promise((resolve) => setTimeout(resolve, 0)) - }) - - afterAll(async () => { - await new Promise((resolve, reject) => serverInstance.close((err) => (err ? reject(err) : resolve(undefined)))) - await server.destroy() - await eventBridge.destroy() - }) - - it('returns healthz', async () => { - await expect(client.get('/healthz')).resolves.toEqual({ - status: 200, - message: 'OK', - }) - }) - - it('returns /api', async () => { - await expect(client.get(apiMountPath)).resolves.toBeDefined() - }) - - it('returns /api/openapi.json', async () => { - const response = await client.get(apiMountPath + '/openapi.json') - expect(response.info.description).toEqual(config.serviceConfig.openApi.info.description) - expect(response.info.title).toEqual(config.serviceConfig.openApi.info.title) - expect(response.info.version).toEqual(config.serviceConfig.openApi.info.version) - expect(response.paths?.['/healthz']).toBeDefined() - }) - - describe('with dynamic routes enabled', () => { - beforeAll(async () => { - // set up the service - const theService = await theServiceV1Service.getInstance(eventBridge, { logger: getLoggerMock().mock }) - await theService.start() - - await new Promise((resolve) => setTimeout(resolve, 5000)) - }) - - it('returns ' + apiMountPath + '/openapi.json', async () => { - const response = await client.get(apiMountPath + '/openapi.json') - - expect(response.paths?.['/healthz']).toBeDefined() - expect(response.paths?.[apiMountPath + '/v1/ping']).toBeDefined() - expect(response.paths?.[apiMountPath + '/v1/unknown']).toBeUndefined() - expect(response.paths?.[apiMountPath + '/v1/error']).toBeDefined() - expect(response.paths?.[apiMountPath + '/v1/post']).toBeDefined() - expect(response.paths?.[apiMountPath + '/v1/patch']).toBeDefined() - expect(response.paths?.[apiMountPath + '/v1/put']).toBeDefined() - expect(response.paths?.[apiMountPath + '/v1/delete']).toBeDefined() - }) - - it('exposes http get endpoint', async () => { - await expect(client.get(apiMountPath + '/v1/ping', { query: { required: 'my_param' } })).resolves.toEqual({ - ping: true, - }) - }) - - it('returns a error on invalid or missing query parameter', async () => { - await expect(client.get(apiMountPath + '/v1/ping')).rejects.toEqual(new Error('Bad Request')) - }) - - it('has a 404 handling', async () => { - await expect(client.get(apiMountPath + '/v1/unknown')).rejects.toEqual(new Error('Not Found')) - }) - - it('returns a error if command returns error', async () => { - await expect(client.get(apiMountPath + '/v1/error')).rejects.toEqual(new Error('Internal Server Error')) - }) - - it('exposes http post endpoint', async () => { - await expect(client.post(apiMountPath + '/v1/post', content)).resolves.toEqual({ payload: content }) - }) - - it('exposes http patch endpoint', async () => { - await expect(client.patch(apiMountPath + '/v1/patch', content)).resolves.toEqual({ payload: content }) - }) - - it('exposes http put endpoint', async () => { - await expect(client.put(apiMountPath + '/v1/put', content)).resolves.toEqual({ payload: content }) - }) - - it('exposes http delete endpoint', async () => { - await expect(client.delete(apiMountPath + '/v1/delete')).resolves.toBeUndefined() - }) - }) + let eventBridge: EventBridge + let server: HonoServiceClass + let serverInstance: ReturnType + const port = 3000 + const client = new HttpClient({ + logger: getLoggerMock().mock, + baseUrl: `http://127.0.0.1:${port}`, + defaultHeaders: { 'content-type': 'application/json; charset=utf-8' }, + }) + + const content = { some: 'content' } + + const apiMountPath = '/api' + + const config = { + logger: getLoggerMock().mock, + serviceConfig: { + enableDynamicRoutes: true, + enableHealthz: true, + apiMountPath, + openApi: { + enabled: true, + info: { + title: 'backend api', + description: 'OpenApi definition for server endpoints', + version: '1.0.0', + }, + }, + }, + } + + beforeAll(async () => { + eventBridge = new DefaultEventBridge({ logger: getLoggerMock().mock }) + await eventBridge.start() + + server = await honoV1Service.getInstance(eventBridge, config) + server.app.get('/api', swaggerUI({ url: '/api/openapi.json' })) + server.setProtectMiddleware(async function (_c, next) { + await next() + }) + await server.start() + + serverInstance = serve({ + fetch: server.app.fetch, + port, + }) + + await new Promise(resolve => setTimeout(resolve, 0)) + }) + + afterAll(async () => { + await new Promise((resolve, reject) => serverInstance.close(err => (err ? reject(err) : resolve(undefined)))) + await server.destroy() + await eventBridge.destroy() + }) + + it('returns healthz', async () => { + await expect(client.get('/healthz')).resolves.toEqual({ + status: 200, + message: 'OK', + }) + }) + + it('returns /api', async () => { + await expect(client.get(apiMountPath)).resolves.toBeDefined() + }) + + it('returns /api/openapi.json', async () => { + const response = await client.get(`${apiMountPath}/openapi.json`) + expect(response.info.description).toEqual(config.serviceConfig.openApi.info.description) + expect(response.info.title).toEqual(config.serviceConfig.openApi.info.title) + expect(response.info.version).toEqual(config.serviceConfig.openApi.info.version) + expect(response.paths?.['/healthz']).toBeDefined() + }) + + describe('with dynamic routes enabled', () => { + beforeAll(async () => { + // set up the service + const theService = await theServiceV1Service.getInstance(eventBridge, { logger: getLoggerMock().mock }) + await theService.start() + + await new Promise(resolve => setTimeout(resolve, 5000)) + }) + + it(`returns ${apiMountPath}/openapi.json`, async () => { + const response = await client.get(`${apiMountPath}/openapi.json`) + + expect(response.paths?.['/healthz']).toBeDefined() + expect(response.paths?.[`${apiMountPath}/v1/ping`]).toBeDefined() + expect(response.paths?.[`${apiMountPath}/v1/unknown`]).toBeUndefined() + expect(response.paths?.[`${apiMountPath}/v1/error`]).toBeDefined() + expect(response.paths?.[`${apiMountPath}/v1/post`]).toBeDefined() + expect(response.paths?.[`${apiMountPath}/v1/patch`]).toBeDefined() + expect(response.paths?.[`${apiMountPath}/v1/put`]).toBeDefined() + expect(response.paths?.[`${apiMountPath}/v1/delete`]).toBeDefined() + }) + + it('exposes http get endpoint', async () => { + await expect(client.get(`${apiMountPath}/v1/ping`, { query: { required: 'my_param' } })).resolves.toEqual({ + ping: true, + }) + }) + + it('returns a error on invalid or missing query parameter', async () => { + await expect(client.get(`${apiMountPath}/v1/ping`)).rejects.toEqual(new Error('Bad Request')) + }) + + it('has a 404 handling', async () => { + await expect(client.get(`${apiMountPath}/v1/unknown`)).rejects.toEqual(new Error('Not Found')) + }) + + it('returns a error if command returns error', async () => { + await expect(client.get(`${apiMountPath}/v1/error`)).rejects.toEqual(new Error('Internal Server Error')) + }) + + it('exposes http post endpoint', async () => { + await expect(client.post(`${apiMountPath}/v1/post`, content)).resolves.toEqual({ payload: content }) + }) + + it('exposes http patch endpoint', async () => { + await expect(client.patch(`${apiMountPath}/v1/patch`, content)).resolves.toEqual({ payload: content }) + }) + + it('exposes http put endpoint', async () => { + await expect(client.put(`${apiMountPath}/v1/put`, content)).resolves.toEqual({ payload: content }) + }) + + it('exposes http delete endpoint', async () => { + await expect(client.delete(`${apiMountPath}/v1/delete`)).resolves.toBeUndefined() + }) + }) }) diff --git a/packages/hono-http-server/test/service/theService/generalTheServiceServiceInfo.ts b/packages/hono-http-server/test/service/theService/generalTheServiceServiceInfo.ts index 45737f772..d4dc4a13a 100644 --- a/packages/hono-http-server/test/service/theService/generalTheServiceServiceInfo.ts +++ b/packages/hono-http-server/test/service/theService/generalTheServiceServiceInfo.ts @@ -1,6 +1,6 @@ import type { ServiceInfoType } from '@purista/core' export const generalTheServiceServiceInfo: Omit = { - serviceName: 'TheService', - serviceDescription: 'a example service', + serviceName: 'TheService', + serviceDescription: 'a example service', } diff --git a/packages/hono-http-server/test/service/theService/v1/command/delete/deleteCommandBuilder.ts b/packages/hono-http-server/test/service/theService/v1/command/delete/deleteCommandBuilder.ts index 073757a35..3c9e39068 100644 --- a/packages/hono-http-server/test/service/theService/v1/command/delete/deleteCommandBuilder.ts +++ b/packages/hono-http-server/test/service/theService/v1/command/delete/deleteCommandBuilder.ts @@ -1,14 +1,14 @@ import { theServiceServiceBuilder } from '../../theServiceServiceBuilder.js' import { - theServiceV1DeleteInputParameterSchema, - theServiceV1DeleteInputPayloadSchema, - theServiceV1DeleteOutputPayloadSchema, + theServiceV1DeleteInputParameterSchema, + theServiceV1DeleteInputPayloadSchema, + theServiceV1DeleteOutputPayloadSchema, } from './schema.js' export const deleteCommandBuilder = theServiceServiceBuilder - .getCommandBuilder('delete', 'provide a dummy command') - .addPayloadSchema(theServiceV1DeleteInputPayloadSchema) - .addParameterSchema(theServiceV1DeleteInputParameterSchema) - .addOutputSchema(theServiceV1DeleteOutputPayloadSchema) - .exposeAsHttpEndpoint('DELETE', 'delete') - .setCommandFunction(async function (_context, _payload, _parameter) {}) + .getCommandBuilder('delete', 'provide a dummy command') + .addPayloadSchema(theServiceV1DeleteInputPayloadSchema) + .addParameterSchema(theServiceV1DeleteInputParameterSchema) + .addOutputSchema(theServiceV1DeleteOutputPayloadSchema) + .exposeAsHttpEndpoint('DELETE', 'delete') + .setCommandFunction(async function (_context, _payload, _parameter) {}) diff --git a/packages/hono-http-server/test/service/theService/v1/command/delete/schema.ts b/packages/hono-http-server/test/service/theService/v1/command/delete/schema.ts index 98a542b73..0fd7a0ae9 100644 --- a/packages/hono-http-server/test/service/theService/v1/command/delete/schema.ts +++ b/packages/hono-http-server/test/service/theService/v1/command/delete/schema.ts @@ -3,7 +3,7 @@ import { z } from 'zod' // define the input parameters export const theServiceV1DeleteInputParameterSchema = extendApi(z.object({}), { - title: 'delete input parameter schema', + title: 'delete input parameter schema', }) // define the input payload @@ -11,5 +11,5 @@ export const theServiceV1DeleteInputPayloadSchema = extendApi(z.any(), { title: // define the output payload export const theServiceV1DeleteOutputPayloadSchema = extendApi(z.void(), { - title: 'put output payload schema', + title: 'put output payload schema', }) diff --git a/packages/hono-http-server/test/service/theService/v1/command/delete/types.ts b/packages/hono-http-server/test/service/theService/v1/command/delete/types.ts index f6d611a99..61a4bfdc9 100644 --- a/packages/hono-http-server/test/service/theService/v1/command/delete/types.ts +++ b/packages/hono-http-server/test/service/theService/v1/command/delete/types.ts @@ -1,9 +1,9 @@ import type { z } from 'zod' import type { - theServiceV1DeleteInputParameterSchema, - theServiceV1DeleteInputPayloadSchema, - theServiceV1DeleteOutputPayloadSchema, + theServiceV1DeleteInputParameterSchema, + theServiceV1DeleteInputPayloadSchema, + theServiceV1DeleteOutputPayloadSchema, } from './schema.js' export type TheServiceV1DeleteInputParameter = z.input diff --git a/packages/hono-http-server/test/service/theService/v1/command/error/errorCommandBuilder.ts b/packages/hono-http-server/test/service/theService/v1/command/error/errorCommandBuilder.ts index 6a86f1f32..16c5ff1da 100644 --- a/packages/hono-http-server/test/service/theService/v1/command/error/errorCommandBuilder.ts +++ b/packages/hono-http-server/test/service/theService/v1/command/error/errorCommandBuilder.ts @@ -1,16 +1,16 @@ import { theServiceServiceBuilder } from '../../theServiceServiceBuilder.js' import { - theServiceV1ErrorInputParameterSchema, - theServiceV1ErrorInputPayloadSchema, - theServiceV1ErrorOutputPayloadSchema, + theServiceV1ErrorInputParameterSchema, + theServiceV1ErrorInputPayloadSchema, + theServiceV1ErrorOutputPayloadSchema, } from './schema.js' export const errorCommandBuilder = theServiceServiceBuilder - .getCommandBuilder('error', 'provide a dummy command') - .addPayloadSchema(theServiceV1ErrorInputPayloadSchema) - .addParameterSchema(theServiceV1ErrorInputParameterSchema) - .addOutputSchema(theServiceV1ErrorOutputPayloadSchema) - .exposeAsHttpEndpoint('GET', 'error') - .setCommandFunction(async function (_context, _payload, _parameter) { - throw new Error('some error') - }) + .getCommandBuilder('error', 'provide a dummy command') + .addPayloadSchema(theServiceV1ErrorInputPayloadSchema) + .addParameterSchema(theServiceV1ErrorInputParameterSchema) + .addOutputSchema(theServiceV1ErrorOutputPayloadSchema) + .exposeAsHttpEndpoint('GET', 'error') + .setCommandFunction(async function (_context, _payload, _parameter) { + throw new Error('some error') + }) diff --git a/packages/hono-http-server/test/service/theService/v1/command/error/schema.ts b/packages/hono-http-server/test/service/theService/v1/command/error/schema.ts index 463b3a027..a1cb48dd6 100644 --- a/packages/hono-http-server/test/service/theService/v1/command/error/schema.ts +++ b/packages/hono-http-server/test/service/theService/v1/command/error/schema.ts @@ -9,5 +9,5 @@ export const theServiceV1ErrorInputPayloadSchema = extendApi(z.undefined(), { ti // define the output payload export const theServiceV1ErrorOutputPayloadSchema = extendApi(z.object({ error: z.boolean() }), { - title: 'error output payload schema', + title: 'error output payload schema', }) diff --git a/packages/hono-http-server/test/service/theService/v1/command/error/types.ts b/packages/hono-http-server/test/service/theService/v1/command/error/types.ts index 2dfd509ea..0981793e8 100644 --- a/packages/hono-http-server/test/service/theService/v1/command/error/types.ts +++ b/packages/hono-http-server/test/service/theService/v1/command/error/types.ts @@ -1,9 +1,9 @@ import type { z } from 'zod' import type { - theServiceV1ErrorInputParameterSchema, - theServiceV1ErrorInputPayloadSchema, - theServiceV1ErrorOutputPayloadSchema, + theServiceV1ErrorInputParameterSchema, + theServiceV1ErrorInputPayloadSchema, + theServiceV1ErrorOutputPayloadSchema, } from './schema.js' export type TheServiceV1ErrorInputParameter = z.input diff --git a/packages/hono-http-server/test/service/theService/v1/command/patch/patchCommandBuilder.ts b/packages/hono-http-server/test/service/theService/v1/command/patch/patchCommandBuilder.ts index 271a8a269..21ccce6de 100644 --- a/packages/hono-http-server/test/service/theService/v1/command/patch/patchCommandBuilder.ts +++ b/packages/hono-http-server/test/service/theService/v1/command/patch/patchCommandBuilder.ts @@ -1,18 +1,18 @@ import { theServiceServiceBuilder } from '../../theServiceServiceBuilder.js' import { - theServiceV1PatchInputParameterSchema, - theServiceV1PatchInputPayloadSchema, - theServiceV1PatchOutputPayloadSchema, + theServiceV1PatchInputParameterSchema, + theServiceV1PatchInputPayloadSchema, + theServiceV1PatchOutputPayloadSchema, } from './schema.js' export const patchCommandBuilder = theServiceServiceBuilder - .getCommandBuilder('patch', 'provide a dummy command') - .addPayloadSchema(theServiceV1PatchInputPayloadSchema) - .addParameterSchema(theServiceV1PatchInputParameterSchema) - .addOutputSchema(theServiceV1PatchOutputPayloadSchema) - .exposeAsHttpEndpoint('PATCH', 'patch') - .setCommandFunction(async function (_context, payload, _parameter) { - return { - payload, - } - }) + .getCommandBuilder('patch', 'provide a dummy command') + .addPayloadSchema(theServiceV1PatchInputPayloadSchema) + .addParameterSchema(theServiceV1PatchInputParameterSchema) + .addOutputSchema(theServiceV1PatchOutputPayloadSchema) + .exposeAsHttpEndpoint('PATCH', 'patch') + .setCommandFunction(async function (_context, payload, _parameter) { + return { + payload, + } + }) diff --git a/packages/hono-http-server/test/service/theService/v1/command/patch/schema.ts b/packages/hono-http-server/test/service/theService/v1/command/patch/schema.ts index cbf9ec2bb..368d58059 100644 --- a/packages/hono-http-server/test/service/theService/v1/command/patch/schema.ts +++ b/packages/hono-http-server/test/service/theService/v1/command/patch/schema.ts @@ -9,5 +9,5 @@ export const theServiceV1PatchInputPayloadSchema = extendApi(z.any(), { title: ' // define the output payload export const theServiceV1PatchOutputPayloadSchema = extendApi(z.any(), { - title: 'patch output payload schema', + title: 'patch output payload schema', }) diff --git a/packages/hono-http-server/test/service/theService/v1/command/patch/types.ts b/packages/hono-http-server/test/service/theService/v1/command/patch/types.ts index bdaa86252..cfbd5f28d 100644 --- a/packages/hono-http-server/test/service/theService/v1/command/patch/types.ts +++ b/packages/hono-http-server/test/service/theService/v1/command/patch/types.ts @@ -1,9 +1,9 @@ import type { z } from 'zod' import type { - theServiceV1PatchInputParameterSchema, - theServiceV1PatchInputPayloadSchema, - theServiceV1PatchOutputPayloadSchema, + theServiceV1PatchInputParameterSchema, + theServiceV1PatchInputPayloadSchema, + theServiceV1PatchOutputPayloadSchema, } from './schema.js' export type TheServiceV1PatchInputParameter = z.input diff --git a/packages/hono-http-server/test/service/theService/v1/command/ping/pingCommandBuilder.ts b/packages/hono-http-server/test/service/theService/v1/command/ping/pingCommandBuilder.ts index d40d31eb9..f16f2c7bc 100644 --- a/packages/hono-http-server/test/service/theService/v1/command/ping/pingCommandBuilder.ts +++ b/packages/hono-http-server/test/service/theService/v1/command/ping/pingCommandBuilder.ts @@ -1,19 +1,19 @@ import { theServiceServiceBuilder } from '../../theServiceServiceBuilder.js' import { - theServiceV1PingInputParameterSchema, - theServiceV1PingInputPayloadSchema, - theServiceV1PingOutputPayloadSchema, + theServiceV1PingInputParameterSchema, + theServiceV1PingInputPayloadSchema, + theServiceV1PingOutputPayloadSchema, } from './schema.js' export const pingCommandBuilder = theServiceServiceBuilder - .getCommandBuilder('ping', 'provide a dummy command') - .addPayloadSchema(theServiceV1PingInputPayloadSchema) - .addParameterSchema(theServiceV1PingInputParameterSchema) - .addOutputSchema(theServiceV1PingOutputPayloadSchema) - .exposeAsHttpEndpoint('GET', 'ping') - .addQueryParameters({ name: 'param', required: false }, { required: true, name: 'required' }) - .setCommandFunction(async function (_context, _payload, _parameter) { - return { - ping: true, - } - }) + .getCommandBuilder('ping', 'provide a dummy command') + .addPayloadSchema(theServiceV1PingInputPayloadSchema) + .addParameterSchema(theServiceV1PingInputParameterSchema) + .addOutputSchema(theServiceV1PingOutputPayloadSchema) + .exposeAsHttpEndpoint('GET', 'ping') + .addQueryParameters({ name: 'param', required: false }, { required: true, name: 'required' }) + .setCommandFunction(async function (_context, _payload, _parameter) { + return { + ping: true, + } + }) diff --git a/packages/hono-http-server/test/service/theService/v1/command/ping/schema.ts b/packages/hono-http-server/test/service/theService/v1/command/ping/schema.ts index b20b855b7..a49cf1bdd 100644 --- a/packages/hono-http-server/test/service/theService/v1/command/ping/schema.ts +++ b/packages/hono-http-server/test/service/theService/v1/command/ping/schema.ts @@ -3,11 +3,11 @@ import { z } from 'zod' // define the input parameters export const theServiceV1PingInputParameterSchema = extendApi( - z.object({ - param: z.string().optional(), - required: z.string(), - }), - { title: 'ping input parameter schema' }, + z.object({ + param: z.string().optional(), + required: z.string(), + }), + { title: 'ping input parameter schema' }, ) // define the input payload @@ -15,5 +15,5 @@ export const theServiceV1PingInputPayloadSchema = extendApi(z.undefined(), { tit // define the output payload export const theServiceV1PingOutputPayloadSchema = extendApi(z.object({ ping: z.boolean() }), { - title: 'ping output payload schema', + title: 'ping output payload schema', }) diff --git a/packages/hono-http-server/test/service/theService/v1/command/ping/types.ts b/packages/hono-http-server/test/service/theService/v1/command/ping/types.ts index babb58f4b..1aacbf07a 100644 --- a/packages/hono-http-server/test/service/theService/v1/command/ping/types.ts +++ b/packages/hono-http-server/test/service/theService/v1/command/ping/types.ts @@ -1,9 +1,9 @@ import type { z } from 'zod' import type { - theServiceV1PingInputParameterSchema, - theServiceV1PingInputPayloadSchema, - theServiceV1PingOutputPayloadSchema, + theServiceV1PingInputParameterSchema, + theServiceV1PingInputPayloadSchema, + theServiceV1PingOutputPayloadSchema, } from './schema.js' export type TheServiceV1PingInputParameter = z.input diff --git a/packages/hono-http-server/test/service/theService/v1/command/post/postCommandBuilder.ts b/packages/hono-http-server/test/service/theService/v1/command/post/postCommandBuilder.ts index c4fab4486..f282b53a2 100644 --- a/packages/hono-http-server/test/service/theService/v1/command/post/postCommandBuilder.ts +++ b/packages/hono-http-server/test/service/theService/v1/command/post/postCommandBuilder.ts @@ -1,18 +1,18 @@ import { theServiceServiceBuilder } from '../../theServiceServiceBuilder.js' import { - theServiceV1PostInputParameterSchema, - theServiceV1PostInputPayloadSchema, - theServiceV1PostOutputPayloadSchema, + theServiceV1PostInputParameterSchema, + theServiceV1PostInputPayloadSchema, + theServiceV1PostOutputPayloadSchema, } from './schema.js' export const postCommandBuilder = theServiceServiceBuilder - .getCommandBuilder('post', 'provide a dummy command') - .addPayloadSchema(theServiceV1PostInputPayloadSchema) - .addParameterSchema(theServiceV1PostInputParameterSchema) - .addOutputSchema(theServiceV1PostOutputPayloadSchema) - .exposeAsHttpEndpoint('POST', 'post') - .setCommandFunction(async function (_context, payload, _parameter) { - return { - payload, - } - }) + .getCommandBuilder('post', 'provide a dummy command') + .addPayloadSchema(theServiceV1PostInputPayloadSchema) + .addParameterSchema(theServiceV1PostInputParameterSchema) + .addOutputSchema(theServiceV1PostOutputPayloadSchema) + .exposeAsHttpEndpoint('POST', 'post') + .setCommandFunction(async function (_context, payload, _parameter) { + return { + payload, + } + }) diff --git a/packages/hono-http-server/test/service/theService/v1/command/post/schema.ts b/packages/hono-http-server/test/service/theService/v1/command/post/schema.ts index a03b3526d..2c2ed82bd 100644 --- a/packages/hono-http-server/test/service/theService/v1/command/post/schema.ts +++ b/packages/hono-http-server/test/service/theService/v1/command/post/schema.ts @@ -9,5 +9,5 @@ export const theServiceV1PostInputPayloadSchema = extendApi(z.any(), { title: 'p // define the output payload export const theServiceV1PostOutputPayloadSchema = extendApi(z.any(), { - title: 'post output payload schema', + title: 'post output payload schema', }) diff --git a/packages/hono-http-server/test/service/theService/v1/command/post/types.ts b/packages/hono-http-server/test/service/theService/v1/command/post/types.ts index 862670f8e..7f78d4ad7 100644 --- a/packages/hono-http-server/test/service/theService/v1/command/post/types.ts +++ b/packages/hono-http-server/test/service/theService/v1/command/post/types.ts @@ -1,9 +1,9 @@ import type { z } from 'zod' import type { - theServiceV1PostInputParameterSchema, - theServiceV1PostInputPayloadSchema, - theServiceV1PostOutputPayloadSchema, + theServiceV1PostInputParameterSchema, + theServiceV1PostInputPayloadSchema, + theServiceV1PostOutputPayloadSchema, } from './schema.js' export type TheServiceV1PostInputParameter = z.input diff --git a/packages/hono-http-server/test/service/theService/v1/command/put/putCommandBuilder.ts b/packages/hono-http-server/test/service/theService/v1/command/put/putCommandBuilder.ts index bb1556790..7595098c1 100644 --- a/packages/hono-http-server/test/service/theService/v1/command/put/putCommandBuilder.ts +++ b/packages/hono-http-server/test/service/theService/v1/command/put/putCommandBuilder.ts @@ -1,18 +1,18 @@ import { theServiceServiceBuilder } from '../../theServiceServiceBuilder.js' import { - theServiceV1PutInputParameterSchema, - theServiceV1PutInputPayloadSchema, - theServiceV1PutOutputPayloadSchema, + theServiceV1PutInputParameterSchema, + theServiceV1PutInputPayloadSchema, + theServiceV1PutOutputPayloadSchema, } from './schema.js' export const putCommandBuilder = theServiceServiceBuilder - .getCommandBuilder('put', 'provide a dummy command') - .addPayloadSchema(theServiceV1PutInputPayloadSchema) - .addParameterSchema(theServiceV1PutInputParameterSchema) - .addOutputSchema(theServiceV1PutOutputPayloadSchema) - .exposeAsHttpEndpoint('PUT', 'put') - .setCommandFunction(async function (_context, payload, _parameter) { - return { - payload, - } - }) + .getCommandBuilder('put', 'provide a dummy command') + .addPayloadSchema(theServiceV1PutInputPayloadSchema) + .addParameterSchema(theServiceV1PutInputParameterSchema) + .addOutputSchema(theServiceV1PutOutputPayloadSchema) + .exposeAsHttpEndpoint('PUT', 'put') + .setCommandFunction(async function (_context, payload, _parameter) { + return { + payload, + } + }) diff --git a/packages/hono-http-server/test/service/theService/v1/command/put/schema.ts b/packages/hono-http-server/test/service/theService/v1/command/put/schema.ts index 77cb18c67..661fea101 100644 --- a/packages/hono-http-server/test/service/theService/v1/command/put/schema.ts +++ b/packages/hono-http-server/test/service/theService/v1/command/put/schema.ts @@ -9,5 +9,5 @@ export const theServiceV1PutInputPayloadSchema = extendApi(z.any(), { title: 'pu // define the output payload export const theServiceV1PutOutputPayloadSchema = extendApi(z.any(), { - title: 'put output payload schema', + title: 'put output payload schema', }) diff --git a/packages/hono-http-server/test/service/theService/v1/command/put/types.ts b/packages/hono-http-server/test/service/theService/v1/command/put/types.ts index bbb0c339c..5414c02a1 100644 --- a/packages/hono-http-server/test/service/theService/v1/command/put/types.ts +++ b/packages/hono-http-server/test/service/theService/v1/command/put/types.ts @@ -1,9 +1,9 @@ import type { z } from 'zod' import type { - theServiceV1PutInputParameterSchema, - theServiceV1PutInputPayloadSchema, - theServiceV1PutOutputPayloadSchema, + theServiceV1PutInputParameterSchema, + theServiceV1PutInputPayloadSchema, + theServiceV1PutOutputPayloadSchema, } from './schema.js' export type TheServiceV1PutInputParameter = z.input diff --git a/packages/hono-http-server/test/service/theService/v1/theServiceServiceBuilder.ts b/packages/hono-http-server/test/service/theService/v1/theServiceServiceBuilder.ts index 2b393ff0c..2f61f4814 100644 --- a/packages/hono-http-server/test/service/theService/v1/theServiceServiceBuilder.ts +++ b/packages/hono-http-server/test/service/theService/v1/theServiceServiceBuilder.ts @@ -5,12 +5,12 @@ import { generalTheServiceServiceInfo } from '../generalTheServiceServiceInfo.js import { theServiceServiceV1ConfigSchema } from './theServiceServiceConfig.js' export const theServiceServiceInfo: ServiceInfoType = { - serviceVersion: '1', - ...generalTheServiceServiceInfo, + serviceVersion: '1', + ...generalTheServiceServiceInfo, } // create a service builder instance and assign service config schema and default config. -export const theServiceServiceBuilder = new ServiceBuilder(theServiceServiceInfo) - .setConfigSchema(theServiceServiceV1ConfigSchema) - .setDefaultConfig({}) +export const theServiceServiceBuilder = new ServiceBuilder(theServiceServiceInfo).setConfigSchema( + theServiceServiceV1ConfigSchema, +) diff --git a/packages/hono-http-server/test/service/theService/v1/theServiceV1Service.ts b/packages/hono-http-server/test/service/theService/v1/theServiceV1Service.ts index a937ad836..128a373d5 100644 --- a/packages/hono-http-server/test/service/theService/v1/theServiceV1Service.ts +++ b/packages/hono-http-server/test/service/theService/v1/theServiceV1Service.ts @@ -13,16 +13,16 @@ import { theServiceServiceBuilder } from './theServiceServiceBuilder.js' // other service config should be done in ./theServiceServiceBuilder.ts file const commandDefinitions: CommandDefinitionList = [ - pingCommandBuilder.getDefinition(), - postCommandBuilder.getDefinition(), - putCommandBuilder.getDefinition(), - patchCommandBuilder.getDefinition(), - deleteCommandBuilder.getDefinition(), - errorCommandBuilder.getDefinition(), + pingCommandBuilder.getDefinition(), + postCommandBuilder.getDefinition(), + putCommandBuilder.getDefinition(), + patchCommandBuilder.getDefinition(), + deleteCommandBuilder.getDefinition(), + errorCommandBuilder.getDefinition(), ] const subscriptionDefinitions: SubscriptionDefinitionList = [] export const theServiceV1Service = theServiceServiceBuilder - .addCommandDefinition(...commandDefinitions) - .addSubscriptionDefinition(...subscriptionDefinitions) + .addCommandDefinition(...commandDefinitions) + .addSubscriptionDefinition(...subscriptionDefinitions) diff --git a/packages/hono-http-server/tsconfig.json b/packages/hono-http-server/tsconfig.json index 42dac6999..a2f689e2f 100644 --- a/packages/hono-http-server/tsconfig.json +++ b/packages/hono-http-server/tsconfig.json @@ -1,21 +1,13 @@ { - "extends": "../../tsconfig.json", - "compilerOptions": { - "outDir": "./dist", - "declaration": true, - "sourceMap": false, - "declarationMap": true, - "types": [ - "vitest/globals", - "node" - ] - }, - "exclude": [ - "./**/*.d.ts" - ], - - "include": [ - "./src/**/*", - "./test/*", - ], -} \ No newline at end of file + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "./dist", + "declaration": true, + "sourceMap": false, + "declarationMap": true, + "types": ["vitest/globals", "node"] + }, + "exclude": ["./**/*.d.ts"], + + "include": ["./src/**/*", "./test/*"] +} diff --git a/packages/hono-http-server/typedoc.json b/packages/hono-http-server/typedoc.json index 355bf0f98..71c4b2283 100644 --- a/packages/hono-http-server/typedoc.json +++ b/packages/hono-http-server/typedoc.json @@ -1,6 +1,5 @@ { - - "extends": ["../../typedoc.base.json"], - "entryPoints": ["src/index.ts"], - "tsconfig": "./tsconfig.json" -} \ No newline at end of file + "extends": ["../../typedoc.base.json"], + "entryPoints": ["src/index.ts"], + "tsconfig": "./tsconfig.json" +} diff --git a/packages/httpserver/README.md b/packages/httpserver/README.md index df849e9f0..d47fc6895 100644 --- a/packages/httpserver/README.md +++ b/packages/httpserver/README.md @@ -4,78 +4,6 @@ > 🚨 This Package is deprecated and maintenance will end soon. > 🚨 Please migrate to `@purista/hono-http-server` package. -The HttpServerService is a service which exposes commands of services as http endpoints. -All exposed commands must be marked as exposed endpoints in the CommandBuilder. - -While the main focus is on development and debug, the HttpServerService will also fit for small projects or running on IoT/edge. - -Under the hood, [fastify](https://www.fastify.io/) is used as basement. -Because of this, the whole [fastify ecosystem](https://www.fastify.io/ecosystem/) can be used and integrated. - -The HttpServerService can also be configured, to provide the OpenApi-UI in browsers. -The OpenApi definitions is created from the CommandBuilder settings of each command. -This means, that there are no additional steps or code required, to provide the OpenApi definition. -It is autogenerated mostly from input and output schema definitions. - -Example usage: - -```typescript -import fastifyStatic from '@fastify/static' -import { DefaultEventBridge, gracefulShutdown, initLogger } from '@purista/core' -import { httpServerV1Service, HttpServerServiceV1Config } from '@purista/httpserver' - -const main = async() => { - const logger = initLogger() - - const eventBridge = new DefaultEventBridge() - - const httpServerConfig: HttpServerServiceV1Config = { - fastify: {}, - port: 8080, - logLevel: 'debug', - domain: 'localhost', - apiMountPath: '/api', - openApi: { - enabled: true, - info: { - title: 'backend api', - description: 'OpenApi definition for server endpoints', - version: '1.0.0', - }, - }, - } - - const httpServerService = await httpServerV1Service.getInstance(eventBridge, { - serviceConfig: httpServerConfig, - }) - - // static file handler - const defaultPublicPath = resolve(__dirname, '..', 'public') - httpServerService.server?.register(fastifyStatic, { - root: defaultPublicPath, - decorateReply: false, - }) - - // start the webserver - await httpServerService.start() - - // and and start your services here - // ... - // ... - - gracefulShutdown(logger, [ - // start with the event bridge to no longer accept incoming messages - eventBridge, - // shut down optional services - // ... - // ... - httpServerService, - ]) -} - -main() -``` - **Visit [purista.dev](https://purista.dev)** **Follow on Twitter [@purista_js](https://twitter.com/purista_js)** diff --git a/packages/httpserver/package.json b/packages/httpserver/package.json index b5311a394..03f090a4d 100644 --- a/packages/httpserver/package.json +++ b/packages/httpserver/package.json @@ -1,74 +1,71 @@ { - "name": "@purista/httpserver", - "version": "1.11.0", - "description": "Simple http server service based on fastify for PURISTA backend framework", - "homepage": "https://purista.dev", - "repository": { - "type": "git", - "url": "git@github.com:sebastianwessel/purista.git" - }, - "author": "Sebastian Wessel", - "license": "ISC", - "type": "module", - "main": "./dist/commonjs/index.js", - "exports": { - "./package.json": "./package.json", - ".": { - "import": { - "types": "./dist/esm/index.d.ts", - "default": "./dist/esm/index.js" - }, - "require": { - "types": "./dist/commonjs/index.d.ts", - "default": "./dist/commonjs/index.js" - } - } - }, - "files": [ - "dist/**/*" - ], - "publishConfig": { - "access": "public" - }, - "engines": { - "node": ">=18.15" - }, - "scripts": { - "lint": "eslint . --ext .ts,.json --cache . --fix", - "test": "vitest", - "build": "rimraf dist && tshy" - }, - "tshy": { - "exclude": [ - "src/**/*.test.ts" - ], - "exports": { - "./package.json": "./package.json", - ".": "./src/index.ts" - } - }, - "devDependencies": { - "@types/node": "^20.11.17", - "@types/qs": "^6.9.11", - "@types/sinon": "^17.0.3", - "@types/swagger-ui-dist": "^3.30.4", - "sinon": "^17.0.1", - "tshy": "^1.11.1", - "vitest": "^1.3.0" - }, - "dependencies": { - "@fastify/compress": "^7.0.0", - "@fastify/cors": "^9.0.0", - "@fastify/helmet": "^11.1.1", - "@fastify/static": "^7.0.1", - "@opentelemetry/api": "^1.7.0", - "@opentelemetry/semantic-conventions": "^1.19.0", - "@purista/core": "*", - "fastify": "^4.26.1", - "openapi3-ts": "4.2.2", - "swagger-ui-dist": "^5.11.7", - "trouter": "^4.0.0" - }, - "peerDependenciesMeta": {}, - "types": "./dist/commonjs/index.d.ts" + "name": "@purista/httpserver", + "version": "1.11.0", + "description": "Simple http server service based on fastify for PURISTA backend framework", + "homepage": "https://purista.dev", + "repository": { + "type": "git", + "url": "git@github.com:puristajs/purista.git" + }, + "author": "Sebastian Wessel", + "license": "ISC", + "type": "module", + "main": "./dist/commonjs/index.js", + "exports": { + "./package.json": "./package.json", + ".": { + "import": { + "types": "./dist/esm/index.d.ts", + "default": "./dist/esm/index.js" + }, + "require": { + "types": "./dist/commonjs/index.d.ts", + "default": "./dist/commonjs/index.js" + } + } + }, + "files": ["dist/**/*"], + "publishConfig": { + "access": "public" + }, + "engines": { + "node": ">=18.15" + }, + "scripts": { + "lint": "npx @biomejs/biome check --write", + "test": "vitest", + "build": "rimraf dist && tshy" + }, + "tshy": { + "exclude": ["src/**/*.test.ts"], + "exports": { + "./package.json": "./package.json", + ".": "./src/index.ts" + } + }, + "devDependencies": { + "@types/node": "^22.5.1", + "@types/qs": "^6.9.15", + "@types/sinon": "^17.0.3", + "@types/swagger-ui-dist": "^3.30.5", + "sinon": "^19.0.2", + "tshy": "^3.0.2", + "vitest": "^3.0.4" + }, + "dependencies": { + "@fastify/compress": "^8.0.1", + "@fastify/cors": "^10.0.1", + "@fastify/helmet": "^13.0.0", + "@fastify/static": "^8.0.3", + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/semantic-conventions": "^1.27.0", + "@purista/core": "*", + "fastify": "^5.1.0", + "openapi3-ts": "4.4.0", + "swagger-ui-dist": "^5.17.14", + "trouter": "^4.0.0" + }, + "peerDependenciesMeta": {}, + "types": "./dist/commonjs/index.d.ts", + "module": "./dist/esm/index.js" } diff --git a/packages/httpserver/src/index.test.ts b/packages/httpserver/src/index.test.ts index c466e0306..cfbd6b6c7 100644 --- a/packages/httpserver/src/index.test.ts +++ b/packages/httpserver/src/index.test.ts @@ -1,11 +1,11 @@ import { httpServerV1Service, puristaVersion } from './index.js' describe('exports httpserver service', () => { - it('has a version', () => { - expect(puristaVersion).toBeDefined() - }) + it('has a version', () => { + expect(puristaVersion).toBeDefined() + }) - it('exports httpServerV1Service', () => { - expect(httpServerV1Service).toBeDefined() - }) + it('exports httpServerV1Service', () => { + expect(httpServerV1Service).toBeDefined() + }) }) diff --git a/packages/httpserver/src/service/httpServer/generalHttpServerServiceInfo.ts b/packages/httpserver/src/service/httpServer/generalHttpServerServiceInfo.ts index 912cba9fd..a4de34c56 100644 --- a/packages/httpserver/src/service/httpServer/generalHttpServerServiceInfo.ts +++ b/packages/httpserver/src/service/httpServer/generalHttpServerServiceInfo.ts @@ -1,6 +1,6 @@ import type { ServiceInfoType } from '@purista/core' export const generalHttpServerServiceInfo: Omit = { - serviceName: 'HttpServer', - serviceDescription: 'provides a http web server for PURISTA', + serviceName: 'HttpServer', + serviceDescription: 'provides a http web server for PURISTA', } diff --git a/packages/httpserver/src/service/httpServer/v1/HttpServerClass.impl.ts b/packages/httpserver/src/service/httpServer/v1/HttpServerClass.impl.ts index 7cf98a671..a1ba7aa58 100644 --- a/packages/httpserver/src/service/httpServer/v1/HttpServerClass.impl.ts +++ b/packages/httpserver/src/service/httpServer/v1/HttpServerClass.impl.ts @@ -3,11 +3,24 @@ import { posix } from 'node:path' import cors from '@fastify/cors' import helmet from '@fastify/helmet' import fastifyStatic from '@fastify/static' -import { context, propagation, SpanKind, SpanStatusCode } from '@opentelemetry/api' +import { SpanKind, SpanStatusCode, context, propagation } from '@opentelemetry/api' import * as api from '@opentelemetry/api' -import { SemanticAttributes } from '@opentelemetry/semantic-conventions' -import type { Command, HttpExposedServiceMeta, ServiceConstructorInput } from '@purista/core' -import { HandledError, safeBind, Service, StatusCode, UnhandledError } from '@purista/core' +import { ATTR_SERVER_ADDRESS } from '@opentelemetry/semantic-conventions/incubating' + +import { + ATTR_HTTP_REQUEST_METHOD, + ATTR_HTTP_RESPONSE_STATUS_CODE, + ATTR_URL_FULL, +} from '@opentelemetry/semantic-conventions' + +import type { + Command, + EmptyObject, + HttpExposedServiceMeta, + ServiceClassTypes, + ServiceConstructorInput, +} from '@purista/core' +import { HandledError, Service, StatusCode, UnhandledError, safeBind } from '@purista/core' import type { FastifyInstance, HTTPMethods } from 'fastify' import fastify from 'fastify' import * as swaggerUi from 'swagger-ui-dist' @@ -24,223 +37,225 @@ import type { BeforeResponseHook } from './types/index.js' * A simple http server based on fastify. * */ -export class HttpServerClass extends Service { - server?: FastifyInstance - - routeDefinitions: HttpExposedServiceMeta>[] = [] - - routes = new Trouter() - - beforeResponse = new Trouter() - - constructor(config: ServiceConstructorInput) { - super(config) - - this.server = fastify({ - ...this.config.fastify, - }) as unknown as FastifyInstance - - this.server - .decorateRequest('principalId', undefined) - .decorateRequest('tenantId', undefined) - .setNotFoundHandler(async (request, reply) => { - const parentContext = propagation.extract(context.active(), request.headers) - await new Promise((resolve) => api.context.with(parentContext, async () => resolve(undefined))) - - await this.startActiveSpan('notFoundHandler', { kind: SpanKind.SERVER }, api.context.active(), async (span) => { - addSpanTags(span, request) - span.setAttribute(SemanticAttributes.HTTP_STATUS_CODE, StatusCode.NotFound) - span.setStatus({ - code: SpanStatusCode.ERROR, - message: 'notFoundHandler', - }) - - addHeaders(span, reply) - const err = new HandledError(StatusCode.NotFound, 'Route not found', { - method: request.method, - route: request.url, - }) - - this.logger.error({ err, ...span.spanContext() }, 'Not found handler') - - if (reply.sent) { - reply.status(StatusCode.NotFound) - reply.send(err.getErrorResponse()) - } - }) - }) - .setErrorHandler(async (err, request, reply) => { - const con = propagation.extract(context.active(), request.headers) - await this.startActiveSpan('errorHandler', { kind: SpanKind.SERVER }, con, async (span) => { - addSpanTags(span, request) - span.recordException(err) - span.setStatus({ - code: SpanStatusCode.ERROR, - message: err.message, - }) - - addHeaders(span, reply) - - propagation.inject(context.active(), reply.headers) - - if (err instanceof HandledError) { - reply.status(err.errorCode) - - span.setAttribute(SemanticAttributes.HTTP_STATUS_CODE, err.errorCode) - return reply.send(err.getErrorResponse()) - } - this.logger.error({ err, ...span.spanContext() }, 'General error handler') - - span.setAttribute(SemanticAttributes.HTTP_STATUS_CODE, StatusCode.InternalServerError) - if (!reply.sent) { - reply.status(StatusCode.InternalServerError) - reply.send(new UnhandledError().getErrorResponse()) - } - }) - }) - - this.server.addHook('onError', async (request, reply, err) => { - const parentContext = propagation.extract(context.active(), request.headers) - await new Promise((resolve) => api.context.with(parentContext, async () => resolve(undefined))) - - await this.startActiveSpan('errorHook', { kind: SpanKind.SERVER }, api.context.active(), async (span) => { - span.setAttribute(SemanticAttributes.HTTP_URL, request.url) - span.setAttribute(SemanticAttributes.HTTP_METHOD, request.method) - span.setAttribute(SemanticAttributes.HTTP_HOST, request.hostname) - - span.recordException(err) - span.setStatus({ - code: SpanStatusCode.ERROR, - message: err.message, - }) - - this.logger.error({ err, ...span.spanContext() }, 'onError hook: General error handler') - }) - - if (!reply.sent) { - reply.status(StatusCode.InternalServerError) - reply.send(new UnhandledError().getErrorResponse()) - } - }) - } - - addBeforeResponse(method: HTTPMethods, pattern: string, handler: BeforeResponseHook) { - this.beforeResponse.add(method.toUpperCase() as Methods, pattern, handler) - } - - async start(): Promise { - if (this.config.enableCompress) { - await this.server?.register(import('@fastify/compress'), this.config.compressOptions) - } - - if (this.config.enableCors) { - await this.server?.register(cors, this.config.corsOptions) - } - - if (this.config.enableHelmet) { - await this.server?.register(helmet, this.config.helmetOptions) - } - - if (this.config.enableHealthz) { - if (this.config.healthzFunction) { - this.server?.get('/healthz', this.config.healthzFunction) - } else { - this.server?.get('/healthz', async (_request, reply) => { - const isEventBridgeReady = await this.eventBridge.isHealthy() - - reply.header('content-type', 'application/json; charset=utf-8') - if (isEventBridgeReady) { - reply.status(StatusCode.OK) - reply.send(new HandledError(StatusCode.OK).getErrorResponse()) - return - } - reply.status(StatusCode.InternalServerError) - reply.send(new HandledError(StatusCode.InternalServerError).getErrorResponse()) - }) - } - } - - const apiBasePath = posix.join(this.config.apiMountPath ?? 'api', '/v*') - - this.server?.all(apiBasePath, async (request, reply) => { - const parentContext = propagation.extract(context.active(), request.headers) - await new Promise((resolve) => api.context.with(parentContext, async () => resolve(undefined))) - - await this.startActiveSpan(request.url, { kind: SpanKind.SERVER }, api.context.active(), async (span) => { - addSpanTags(span, request) - - addHeaders(span, reply) - - const match = (request.params as Record)['*'] - const path = posix.join(this.config.apiMountPath ?? 'api', `v${match}`) - - const route = this.routes.find(request.method as Methods, path) - const firstHandler = route.handlers[0] - if (!firstHandler) { - this.logger.debug({ method: request.method, url: request.url }, 'Route not found') - const err = new HandledError(StatusCode.NotFound) - span.recordException(err) - span.setStatus({ - code: SpanStatusCode.ERROR, - message: err.message, - }) - span.setAttribute(SemanticAttributes.HTTP_STATUS_CODE, StatusCode.NotFound) - reply.code(StatusCode.NotFound) - return err.getErrorResponse() - } - - await firstHandler(request, reply, route.params) - }) - }) - - if (this.config.openApi?.enabled) { - const apiUrl = this.config.openApi?.path ? this.config.openApi.path : this.config.apiMountPath - if (!apiUrl) { - throw new Error('Configuration error! When openApi is enabled you need to set openApi.path or apiMountPath') - } - const prefix = posix.join(apiUrl, '/assets') - - await this.server?.register(fastifyStatic, { - root: swaggerUi.absolutePath(), - prefix, - decorateReply: false, // the reply decorator has been added by the first plugin registration - }) - - OPEN_API_ROUTE_FUNCTIONS.forEach((route) => { - const def = safeBind(route, this)() - this.server?.route(def) - this.logger.debug(`add route ${def.method} ${def.url}`) - }) - } - - await super.start() - this.server?.listen({ - port: this.config.port, - host: this.config.host, - }) - this.logger.info( - { domain: this.config.domain, port: this.config.port }, - `http server listen on ${this.config.domain} ${this.config.port}`, - ) - } - - async invoke( - input: Omit, - endpoint: string, - ): Promise { - return this.eventBridge.invoke({ - sender: { - serviceName: this.serviceInfo.serviceName, - serviceVersion: this.serviceInfo.serviceVersion, - serviceTarget: `$$endpoint:${endpoint}`, - instanceId: this.eventBridge.instanceId, - }, - ...input, - }) - } - - async destroy() { - await this.server?.close() - await super.destroy() - } +export class HttpServerClass extends Service< + ServiceClassTypes +> { + server?: FastifyInstance + + routeDefinitions: HttpExposedServiceMeta>[] = [] + + routes = new Trouter() + + beforeResponse = new Trouter() + + constructor(config: ServiceConstructorInput>) { + super(config) + + this.server = fastify({ + ...this.config.fastify, + }) as unknown as FastifyInstance + + this.server + .decorateRequest('principalId', undefined) + .decorateRequest('tenantId', undefined) + .setNotFoundHandler(async (request, reply) => { + const parentContext = propagation.extract(context.active(), request.headers) + await new Promise(resolve => api.context.with(parentContext, async () => resolve(undefined))) + + await this.startActiveSpan('notFoundHandler', { kind: SpanKind.SERVER }, api.context.active(), async span => { + addSpanTags(span, request) + span.setAttribute(ATTR_HTTP_RESPONSE_STATUS_CODE, StatusCode.NotFound) + span.setStatus({ + code: SpanStatusCode.ERROR, + message: 'notFoundHandler', + }) + + addHeaders(span, reply) + const err = new HandledError(StatusCode.NotFound, 'Route not found', { + method: request.method, + route: request.url, + }) + + this.logger.error({ err, ...span.spanContext() }, 'Not found handler') + + if (reply.sent) { + reply.status(StatusCode.NotFound) + reply.send(err.getErrorResponse()) + } + }) + }) + .setErrorHandler(async (err, request, reply) => { + const con = propagation.extract(context.active(), request.headers) + await this.startActiveSpan('errorHandler', { kind: SpanKind.SERVER }, con, async span => { + addSpanTags(span, request) + span.recordException(err) + span.setStatus({ + code: SpanStatusCode.ERROR, + message: err.message, + }) + + addHeaders(span, reply) + + propagation.inject(context.active(), reply.headers) + + if (err instanceof HandledError) { + reply.status(err.errorCode) + + span.setAttribute(ATTR_HTTP_RESPONSE_STATUS_CODE, err.errorCode) + return reply.send(err.getErrorResponse()) + } + this.logger.error({ err, ...span.spanContext() }, 'General error handler') + + span.setAttribute(ATTR_HTTP_RESPONSE_STATUS_CODE, StatusCode.InternalServerError) + if (!reply.sent) { + reply.status(StatusCode.InternalServerError) + reply.send(new UnhandledError().getErrorResponse()) + } + }) + }) + + this.server.addHook('onError', async (request, reply, err) => { + const parentContext = propagation.extract(context.active(), request.headers) + await new Promise(resolve => api.context.with(parentContext, async () => resolve(undefined))) + + await this.startActiveSpan('errorHook', { kind: SpanKind.SERVER }, api.context.active(), async span => { + span.setAttribute(ATTR_URL_FULL, request.url) + span.setAttribute(ATTR_HTTP_REQUEST_METHOD, request.method) + span.setAttribute(ATTR_SERVER_ADDRESS, request.hostname) + + span.recordException(err) + span.setStatus({ + code: SpanStatusCode.ERROR, + message: err.message, + }) + + this.logger.error({ err, ...span.spanContext() }, 'onError hook: General error handler') + }) + + if (!reply.sent) { + reply.status(StatusCode.InternalServerError) + reply.send(new UnhandledError().getErrorResponse()) + } + }) + } + + addBeforeResponse(method: HTTPMethods, pattern: string, handler: BeforeResponseHook) { + this.beforeResponse.add(method.toUpperCase() as Methods, pattern, handler) + } + + async start(): Promise { + if (this.config.enableCompress) { + await this.server?.register(import('@fastify/compress'), this.config.compressOptions) + } + + if (this.config.enableCors) { + await this.server?.register(cors, this.config.corsOptions) + } + + if (this.config.enableHelmet) { + await this.server?.register(helmet, this.config.helmetOptions ?? {}) + } + + if (this.config.enableHealthz) { + if (this.config.healthzFunction) { + this.server?.get('/healthz', this.config.healthzFunction) + } else { + this.server?.get('/healthz', async (_request, reply) => { + const isEventBridgeReady = await this.eventBridge.isHealthy() + + reply.header('content-type', 'application/json; charset=utf-8') + if (isEventBridgeReady) { + reply.status(StatusCode.OK) + reply.send(new HandledError(StatusCode.OK).getErrorResponse()) + return + } + reply.status(StatusCode.InternalServerError) + reply.send(new HandledError(StatusCode.InternalServerError).getErrorResponse()) + }) + } + } + + const apiBasePath = posix.join(this.config.apiMountPath ?? 'api', '/v*') + + this.server?.all(apiBasePath, async (request, reply) => { + const parentContext = propagation.extract(context.active(), request.headers) + await new Promise(resolve => api.context.with(parentContext, async () => resolve(undefined))) + + await this.startActiveSpan(request.url, { kind: SpanKind.SERVER }, api.context.active(), async span => { + addSpanTags(span, request) + + addHeaders(span, reply) + + const match = (request.params as Record)['*'] + const path = posix.join(this.config.apiMountPath ?? 'api', `v${match}`) + + const route = this.routes.find(request.method as Methods, path) + const firstHandler = route.handlers[0] + if (!firstHandler) { + this.logger.debug({ method: request.method, url: request.url }, 'Route not found') + const err = new HandledError(StatusCode.NotFound) + span.recordException(err) + span.setStatus({ + code: SpanStatusCode.ERROR, + message: err.message, + }) + span.setAttribute(ATTR_HTTP_RESPONSE_STATUS_CODE, StatusCode.NotFound) + reply.code(StatusCode.NotFound) + return err.getErrorResponse() + } + + await firstHandler(request, reply, route.params) + }) + }) + + if (this.config.openApi?.enabled) { + const apiUrl = this.config.openApi?.path ? this.config.openApi.path : this.config.apiMountPath + if (!apiUrl) { + throw new Error('Configuration error! When openApi is enabled you need to set openApi.path or apiMountPath') + } + const prefix = posix.join(apiUrl, '/assets') + + await this.server?.register(fastifyStatic, { + root: swaggerUi.absolutePath(), + prefix, + decorateReply: false, // the reply decorator has been added by the first plugin registration + }) + + for (const route of OPEN_API_ROUTE_FUNCTIONS) { + const def = safeBind(route, this)() + this.server?.route(def) + this.logger.debug(`add route ${def.method} ${def.url}`) + } + } + + await super.start() + this.server?.listen({ + port: this.config.port, + host: this.config.host, + }) + this.logger.info( + { domain: this.config.domain, port: this.config.port }, + `http server listen on ${this.config.domain} ${this.config.port}`, + ) + } + + async invoke( + input: Omit, + endpoint: string, + ): Promise { + return this.eventBridge.invoke({ + sender: { + serviceName: this.serviceInfo.serviceName, + serviceVersion: this.serviceInfo.serviceVersion, + serviceTarget: `$$endpoint:${endpoint}`, + instanceId: this.eventBridge.instanceId, + }, + ...input, + }) + } + + async destroy() { + await this.server?.close() + await super.destroy() + } } diff --git a/packages/httpserver/src/service/httpServer/v1/httpServerServiceConfig.ts b/packages/httpserver/src/service/httpServer/v1/httpServerServiceConfig.ts index 36b413d98..62db64bda 100644 --- a/packages/httpserver/src/service/httpServer/v1/httpServerServiceConfig.ts +++ b/packages/httpserver/src/service/httpServer/v1/httpServerServiceConfig.ts @@ -10,89 +10,91 @@ import { z } from 'zod' export const OPENAPI_DEFAULT_MOUNT_PATH = '/api' export const OPENAPI_DEFAULT_INFO = { - title: 'Server api', - description: 'OpenApi definition for server endpoints', - version: '1.0.0', + title: 'Server api', + description: 'OpenApi definition for server endpoints', + version: '1.0.0', } export const ExternalDocumentationObjectSchema = z.object({ - description: z.string().optional(), - url: z.string().url(), + description: z.string().optional(), + url: z.string().url(), }) export const TagObjectSchema = z.object({ - name: z.string(), - description: z.string().optional(), - externalDocs: ExternalDocumentationObjectSchema.optional(), + name: z.string(), + description: z.string().optional(), + externalDocs: ExternalDocumentationObjectSchema.optional(), }) export const FastifyHelmetOptionsSchema = z.object({ - enableCSPNonces: z.boolean().optional(), - global: z.boolean().optional(), + enableCSPNonces: z.boolean().optional(), + global: z.boolean().optional(), }) export const InfoObjectSchema = z.object({ - title: z.string().default('PURISTA'), - description: z.string().default('OpenApi definition for server endpoints'), - termsOfService: z.string().optional(), - contact: z - .object({ - name: z.string().optional(), - url: z.string().optional(), - email: z.string().optional(), - }) - .optional(), - license: z - .object({ - name: z.string(), - url: z.string().optional(), - }) - .optional(), - version: z.string().default('1.0.0'), + title: z.string().default('PURISTA'), + description: z.string().default('OpenApi definition for server endpoints'), + termsOfService: z.string().optional(), + contact: z + .object({ + name: z.string().optional(), + url: z.string().optional(), + email: z.string().optional(), + }) + .optional(), + license: z + .object({ + name: z.string(), + url: z.string().optional(), + }) + .optional(), + version: z.string().default('1.0.0'), }) export const ServerObjectSchema = z.object({ - url: z.string(), - description: z.string().optional(), - variables: z.any().optional(), + url: z.string(), + description: z.string().optional(), + variables: z.any().optional(), }) export const httpServerServiceV1ConfigSchema = z.object({ - fastify: z.any(), // - logLevel: z.enum(['info', 'error', 'warn', 'debug', 'trace', 'fatal']).optional(), - port: z.number().int().min(1), - host: z.string().optional().default('0.0.0.0'), - domain: z.string().optional().default('localhost'), - uploadDir: z.string().optional(), - cookieSecret: z.string().optional(), - apiMountPath: z.string().optional(), - enableHelmet: z.boolean().optional().default(false), - enableHealthz: z.boolean().optional().default(true), - healthzFunction: z.function().args(z.any(), z.any()).returns(z.promise(z.void())).optional(), - helmetOptions: FastifyHelmetOptionsSchema.optional(), - enableCompress: z.boolean().optional().default(false), - compressOptions: z.any().optional(), - enableCors: z.boolean().optional().default(false), - corsOptions: z.any().optional(), - traceHeaderField: z.string().default('x-trace-id'), - openApi: z - .object({ - enabled: z.boolean().optional().default(true), - path: z.string().optional().default('/api'), - info: InfoObjectSchema, - servers: z.array(ServerObjectSchema).optional(), - components: z.any().optional(), - security: z.array(z.any()).optional(), - externalDocs: ExternalDocumentationObjectSchema.optional(), - tags: z.array(TagObjectSchema).optional(), - paths: z.record(z.string(), z.record(z.string(), z.any())).optional(), - }) - .optional(), + fastify: z.any().default({ + ignoreTrailingSlash: true, + }), // + logLevel: z.enum(['info', 'error', 'warn', 'debug', 'trace', 'fatal']).optional().default('warn'), + port: z.number().int().min(1).default(9090), + host: z.string().optional().default('0.0.0.0'), + domain: z.string().optional().default('localhost'), + uploadDir: z.string().optional(), + cookieSecret: z.string().optional(), + apiMountPath: z.string().optional().default('/api'), + enableHelmet: z.boolean().optional().default(true), + enableHealthz: z.boolean().optional().default(true), + healthzFunction: z.function().args(z.any(), z.any()).returns(z.promise(z.void())).optional(), + helmetOptions: FastifyHelmetOptionsSchema.optional(), + enableCompress: z.boolean().optional().default(false), + compressOptions: z.any().optional(), + enableCors: z.boolean().optional().default(false), + corsOptions: z.any().optional(), + traceHeaderField: z.string().default('x-trace-id'), + openApi: z + .object({ + enabled: z.boolean().optional().default(true), + path: z.string().optional().default(OPENAPI_DEFAULT_MOUNT_PATH), + info: InfoObjectSchema, + servers: z.array(ServerObjectSchema).optional(), + components: z.any().optional(), + security: z.array(z.any()).optional(), + externalDocs: ExternalDocumentationObjectSchema.optional(), + tags: z.array(TagObjectSchema).optional(), + paths: z.record(z.string(), z.record(z.string(), z.any())).optional(), + }) + .optional(), }) export type HttpServerServiceV1ConfigRaw = z.input export type HttpServerServiceV1Config = HttpServerServiceV1ConfigRaw & { - compressOptions?: FastifyCompressOptions - fastify: Partial & Partial> - corsOptions?: FastifyCorsOptions + compressOptions?: FastifyCompressOptions + fastify: Partial & Partial> + corsOptions?: FastifyCorsOptions } diff --git a/packages/httpserver/src/service/httpServer/v1/httpServerV1Service.test.ts b/packages/httpserver/src/service/httpServer/v1/httpServerV1Service.test.ts index 51eacd7ae..be7e95fdc 100644 --- a/packages/httpserver/src/service/httpServer/v1/httpServerV1Service.test.ts +++ b/packages/httpserver/src/service/httpServer/v1/httpServerV1Service.test.ts @@ -1,11 +1,7 @@ import { httpServerV1Service as service } from './httpServerV1Service.js' describe('service httpServer version 1', () => { - it('has valid commands', () => { - service.validateCommandDefinitions() - }) - - it('has valid subscriptions', () => { - service.validateSubscriptionDefinitions() - }) + it('has valid service setup', () => { + service.testServiceSetup() + }) }) diff --git a/packages/httpserver/src/service/httpServer/v1/httpServerV1Service.ts b/packages/httpserver/src/service/httpServer/v1/httpServerV1Service.ts index 24d16c30b..ddc15f240 100644 --- a/packages/httpserver/src/service/httpServer/v1/httpServerV1Service.ts +++ b/packages/httpserver/src/service/httpServer/v1/httpServerV1Service.ts @@ -10,12 +10,12 @@ import { serviceCommandsToRestApiSubscriptionBuilder } from './subscription/serv const commandDefinitions: CommandDefinitionList = [] const subscriptionDefinitions: SubscriptionDefinitionList = [ - serviceCommandsToRestApiSubscriptionBuilder.getDefinition(), + serviceCommandsToRestApiSubscriptionBuilder.getDefinition(), ] /** * @deprecated Since version 1.10.0. Use {@purista/hono-http-server} instead. */ export const httpServerV1Service = httpServerV1ServiceBuilder - .addCommandDefinition(...commandDefinitions) - .addSubscriptionDefinition(...subscriptionDefinitions) + .addCommandDefinition(...commandDefinitions) + .addSubscriptionDefinition(...subscriptionDefinitions) diff --git a/packages/httpserver/src/service/httpServer/v1/httpServerV1ServiceBuilder.ts b/packages/httpserver/src/service/httpServer/v1/httpServerV1ServiceBuilder.ts index e14c5d9c4..569e8778b 100644 --- a/packages/httpserver/src/service/httpServer/v1/httpServerV1ServiceBuilder.ts +++ b/packages/httpserver/src/service/httpServer/v1/httpServerV1ServiceBuilder.ts @@ -3,46 +3,15 @@ import { ServiceBuilder } from '@purista/core' import { generalHttpServerServiceInfo } from '../generalHttpServerServiceInfo.js' import { HttpServerClass } from './HttpServerClass.impl.js' -import { - httpServerServiceV1ConfigSchema, - OPENAPI_DEFAULT_INFO, - OPENAPI_DEFAULT_MOUNT_PATH, -} from './httpServerServiceConfig.js' +import { httpServerServiceV1ConfigSchema } from './httpServerServiceConfig.js' export const httpServerServiceInfo: ServiceInfoType = { - serviceVersion: '1', - ...generalHttpServerServiceInfo, + serviceVersion: '1', + ...generalHttpServerServiceInfo, } // create a service builder instance and assign service config schema and default config. export const httpServerV1ServiceBuilder = new ServiceBuilder(httpServerServiceInfo) - .setConfigSchema(httpServerServiceV1ConfigSchema) - .setDefaultConfig({ - fastify: { - ignoreTrailingSlash: true, - }, - logLevel: 'warn', - port: 9090, - host: '0.0.0.0', - domain: 'localhost', - uploadDir: undefined, - cookieSecret: undefined, - apiMountPath: '/api', - enableHelmet: true, - enableCompress: true, - enableHealthz: true, - healthzFunction: undefined, - enableCors: false, - helmetOptions: undefined, - compressOptions: undefined, - corsOptions: undefined, - traceHeaderField: 'x-trace-id', - openApi: { - enabled: true, - path: OPENAPI_DEFAULT_MOUNT_PATH, - info: OPENAPI_DEFAULT_INFO, - paths: undefined, - }, - }) - .setCustomClass(HttpServerClass) + .setConfigSchema(httpServerServiceV1ConfigSchema) + .setCustomClass(HttpServerClass) diff --git a/packages/httpserver/src/service/httpServer/v1/routes/openapi/getOpenApiDocuIndex.impl.ts b/packages/httpserver/src/service/httpServer/v1/routes/openapi/getOpenApiDocuIndex.impl.ts index 907940f31..9eaa9e5f1 100644 --- a/packages/httpserver/src/service/httpServer/v1/routes/openapi/getOpenApiDocuIndex.impl.ts +++ b/packages/httpserver/src/service/httpServer/v1/routes/openapi/getOpenApiDocuIndex.impl.ts @@ -5,18 +5,18 @@ import type { HttpServerServiceV1ConfigRaw } from '../../httpServerServiceConfig import { getIndexHtml } from './getIndexHtml.impl.js' export const getOpenApiDocuIndex = function (this: HttpServerClass): RouteOptions { - const path = (this.config.openApi?.path ? this.config.openApi.path : this.config.apiMountPath) as string - const url = `${path}` + const path = (this.config.openApi?.path ? this.config.openApi.path : this.config.apiMountPath) as string + const url = `${path}` - const handler: RouteHandlerMethod = (_reqest, reply) => { - reply.header('content-type', 'text/html; charset=utf-8') + const handler: RouteHandlerMethod = (_reqest, reply) => { + reply.header('content-type', 'text/html; charset=utf-8') - return getIndexHtml(path) - } + return getIndexHtml(path) + } - return { - method: 'GET', - url, - handler, - } + return { + method: 'GET', + url, + handler, + } } diff --git a/packages/httpserver/src/service/httpServer/v1/routes/openapi/getOpenApiDocuJsInit.impl.ts b/packages/httpserver/src/service/httpServer/v1/routes/openapi/getOpenApiDocuJsInit.impl.ts index 31ba74a8b..f7a9c119b 100644 --- a/packages/httpserver/src/service/httpServer/v1/routes/openapi/getOpenApiDocuJsInit.impl.ts +++ b/packages/httpserver/src/service/httpServer/v1/routes/openapi/getOpenApiDocuJsInit.impl.ts @@ -7,18 +7,18 @@ import type { HttpServerServiceV1ConfigRaw } from '../../httpServerServiceConfig import { getJsInit } from './getJsInit.impl.js' export const getOpenApiDocuJsInit = function (this: HttpServerClass): RouteOptions { - const path = (this.config.openApi?.path ? this.config.openApi.path : this.config.apiMountPath) as string - const url = posix.join(path, '/initializer.js') + const path = (this.config.openApi?.path ? this.config.openApi.path : this.config.apiMountPath) as string + const url = posix.join(path, '/initializer.js') - const handler: RouteHandlerMethod = (_request, reply) => { - reply.header('content-type', 'text/javascript; charset=utf-8') + const handler: RouteHandlerMethod = (_request, reply) => { + reply.header('content-type', 'text/javascript; charset=utf-8') - return getJsInit(path) - } + return getJsInit(path) + } - return { - method: 'GET', - url, - handler, - } + return { + method: 'GET', + url, + handler, + } } diff --git a/packages/httpserver/src/service/httpServer/v1/routes/openapi/getOpenApiJson.impl.ts b/packages/httpserver/src/service/httpServer/v1/routes/openapi/getOpenApiJson.impl.ts index dfb20fc27..d486dac4d 100644 --- a/packages/httpserver/src/service/httpServer/v1/routes/openapi/getOpenApiJson.impl.ts +++ b/packages/httpserver/src/service/httpServer/v1/routes/openapi/getOpenApiJson.impl.ts @@ -17,354 +17,359 @@ import { OPENAPI_DEFAULT_INFO } from '../../httpServerServiceConfig.js' * @returns A route definition for the openApi.json file */ export const getOpenApiJson = function (this: HttpServerClass): RouteOptions { - const paths: Record> = this.config.openApi?.paths ?? {} - - const p = (this.config.openApi?.path ? this.config.openApi.path : this.config.apiMountPath) as string - const url = posix.join(p, '/openapi.json') - - const info = { ...OPENAPI_DEFAULT_INFO, ...this.config.openApi?.info } - const servers = this.config.openApi?.servers ?? [ - { url: `${this.config.fastify?.https ? 'https' : 'http'}://${this.config.domain}:${this.config.port}` }, - ] - const components = this.config.openApi?.components - const security = this.config.openApi?.security - const externalDocs = this.config.openApi?.externalDocs - const tags = this.config.openApi?.tags - const isHealthzEnabled = this.config.enableHealthz - - const logger = this.logger.getChildLogger({ - serviceName: this.info.serviceName, - serviceVersion: this.info.serviceVersion, - serviceTarget: 'openApiJson', - }) - - const handler: RouteHandlerMethod = (_request, reply) => { - const json: OpenAPIObject = { - openapi: '3.0.3', - info, - servers, - paths, - components, - security, - tags, - externalDocs, - } - - let securitySchema: unknown[] = [] - - if (components?.securitySchemes) { - securitySchema = Object.keys(components.securitySchemes).map((name) => ({ [name]: [] })) - } - - const getErrorName = (code: StatusCode) => - StatusCode[code] - .replace(/[A-Z]/g, (letter) => ` ${letter}`) - .replace(/^./, (str) => { - return str.toUpperCase() - }) - .trim() - .replace(/^O K$/g, 'OK') - - const getErrorResponseSchema = (code: StatusCode, message: string, schema?: SchemaObject) => { - return { - type: 'object', - - properties: { - status: { - type: 'number', - min: 100, - title: 'the error status code', - example: code, - }, - message: { - type: 'string', - title: 'the error message', - example: message, - }, - data: schema, - traceparent: { - type: 'string', - title: 'w3c compliant unique traceparent for this request', - example: 'd5dbb17eec16e3c9fce9cf8adc766999', - externalDocs: 'https://www.w3.org/TR/trace-context/#traceparent-header-field-values', - }, - }, - required: ['status', 'message'], - } - } - - const findPathParamsRegex = /:[^:/]+/gm - - this.routeDefinitions.forEach((entry) => { - const expose = entry.expose - const definition = expose.http - - let m - const routeParams: string[] = [] - while ((m = findPathParamsRegex.exec(definition.path)) !== null) { - if (m.index === findPathParamsRegex.lastIndex) { - findPathParamsRegex.lastIndex++ - } - - routeParams.push(...m.map((name) => name)) - } - - const pathParams: ParameterObject[] = routeParams.map((pathParamName): ParameterObject => { - const name = pathParamName.replace('?', '').replace(':', '') - const required = !pathParamName.endsWith('?') - - const schema = expose.parameter?.properties?.[name] - - if (!schema) { - logger.warn( - `${definition.method} ${definition.path}: Path parameter ${name} is not in parameter schema and will not be available in service function`, - ) - } - - if (!!schema && isReferenceObject(schema)) { - return { - in: 'path', - name, - required, - ...schema, - } - } - - return { - in: 'path', - name, - required, - schema, - description: schema?.description ?? schema?.title, - } - }) - - const queryParams = - definition.openApi?.query?.map((queryParam): ParameterObject => { - const name = queryParam.name - const schema = expose.parameter?.properties?.[name] - const required = queryParam.required - - if (!schema) { - logger.warn( - `${definition.method} ${definition.path}: Query parameter ${name} is not in parameter schema and will not be available in service function`, - ) - } - - if (!!schema && isReferenceObject(schema)) { - return { - in: 'query', - name, - required, - ...schema, - } - } - - return { - in: 'query', - name, - required, - schema, - description: schema?.description ?? schema?.title, - } - }) ?? [] - - let path = definition.path - routeParams.forEach((pathParamName) => { - const name = pathParamName.replace('?', '').replace(':', '') - path = path.replace(pathParamName, `{${name}}`) - }) - - let requestBody: RequestBodyObject | undefined - - if (['POST', 'PATCH', 'PUT'].includes(definition.method)) { - requestBody = { - content: { - [expose.contentTypeRequest ?? 'application/json']: { - schema: expose.inputPayload, - }, - }, - } - } - - const inputValidationFailed: SchemaObject = { - type: 'array', - items: { - type: 'object', - properties: { - validation: { - type: 'string', - example: 'invalid_string', - }, - code: { - type: 'string', - example: 'invalid_string', - }, - message: { - type: 'string', - example: 'String must contain at least 3 character(s)', - }, - expected: { - type: 'string', - example: 'string', - }, - received: { - type: 'string', - example: 'object', - }, - keys: { - type: 'array', - items: { - type: 'string', - }, - }, - minimum: { - type: 'number', - example: 3, - }, - maximum: { - type: 'number', - example: 32, - }, - path: { - type: 'array', - items: { - type: 'string', - example: 'username', - }, - }, - }, - required: ['message', 'code'], - }, - } - - const errorResponses: Record = {} - - if (expose.inputPayload) { - errorResponses[400] = { - description: getErrorName(400), - content: { - 'application/json': { - schema: getErrorResponseSchema(400, 'input validation failed', inputValidationFailed), - }, - }, - } - } - - if (expose.parameter && pathParams.length > 0) { - errorResponses[404] = { - description: getErrorName(404), - content: { - 'application/json': { - schema: getErrorResponseSchema(404, 'ressource for given id does not exist'), - }, - }, - } - } - - if (securitySchema.length > 0 && definition.openApi?.isSecure) { - errorResponses[401] = { - description: getErrorName(401), - content: { - 'application/json': { - schema: getErrorResponseSchema(401, 'authentication required'), - }, - }, - } - } - - definition.openApi?.additionalStatusCodes - ?.filter((code) => !Object.keys(errorResponses).includes(code.toString())) - .forEach((code) => { - errorResponses[code] = { - description: getErrorName(code), - content: { - 'application/json': { - schema: getErrorResponseSchema(code, getErrorName(code)), - }, - }, - } - }) - - const traceIdParameter: ParameterObject = { - in: 'header', - required: false, - name: this.config.traceHeaderField ?? 'x-trace-id', - schema: { type: 'string' }, - example: '022bcd32-0a7c-4635-90ce-7940d0b9793f', - description: 'TraceID which can be used by business logic', - } - - const traceParent: ParameterObject = { - in: 'header', - required: false, - name: 'traceparent', - schema: { type: 'string' }, - description: 'see: https://www.w3.org/TR/trace-context/#traceparent-header-field-values', - } - - paths[path] = { - ...paths[path], - [definition.method.toLowerCase()]: { - deprecated: expose.deprecated, - security: securitySchema.length > 0 && definition.openApi?.isSecure ? securitySchema : [], - description: definition.openApi?.description, - summary: definition.openApi?.summary, - parameters: [...pathParams, ...queryParams, traceIdParameter, traceParent], - tags: definition.openApi?.tags, - operationId: definition.openApi?.operationId, - requestBody, - responses: { - [expose.outputPayload ? 200 : 204]: { - description: definition.openApi?.description, - content: { - [expose.contentTypeResponse ?? 'application/json']: { - schema: expose.outputPayload, - }, - }, - }, - ...errorResponses, - }, - }, - } - }) - - if (isHealthzEnabled) { - if (!json.paths) { - json.paths = {} - } - json.paths['/healthz'] = { - get: { - description: 'indicates if the http server service is healthy', - responses: { - 200: { - description: 'http server service is up and running and successfully connected to event bridge', - content: { - 'application/json': { - schema: getErrorResponseSchema(200, getErrorName(200)), - }, - }, - }, - 500: { - description: 'http server service is not up and running or not successfully connected to event bridge', - content: { - 'application/json': { - schema: getErrorResponseSchema(500, getErrorName(500)), - }, - }, - }, - }, - }, - } - } - - reply.header('content-type', 'application/json; charset=utf-8') - return json - } - - return { - method: 'GET', - url, - handler, - } + const paths: Record> = this.config.openApi?.paths ?? {} + + const p = (this.config.openApi?.path ? this.config.openApi.path : this.config.apiMountPath) as string + const url = posix.join(p, '/openapi.json') + + const info = { ...OPENAPI_DEFAULT_INFO, ...this.config.openApi?.info } + const servers = this.config.openApi?.servers ?? [ + { url: `${this.config.fastify?.https ? 'https' : 'http'}://${this.config.domain}:${this.config.port}` }, + ] + const components = this.config.openApi?.components + const security = this.config.openApi?.security + const externalDocs = this.config.openApi?.externalDocs + const tags = this.config.openApi?.tags + const isHealthzEnabled = this.config.enableHealthz + + const logger = this.logger.getChildLogger({ + serviceName: this.info.serviceName, + serviceVersion: this.info.serviceVersion, + serviceTarget: 'openApiJson', + }) + + const handler: RouteHandlerMethod = (_request, reply) => { + const json: OpenAPIObject = { + openapi: '3.0.3', + info, + servers, + paths, + components, + security, + tags, + externalDocs, + } + + let securitySchema: unknown[] = [] + + if (components?.securitySchemes) { + securitySchema = Object.keys(components.securitySchemes).map(name => ({ [name]: [] })) + } + + const getErrorName = (code: StatusCode) => + StatusCode[code] + .replace(/[A-Z]/g, letter => ` ${letter}`) + .replace(/^./, str => { + return str.toUpperCase() + }) + .trim() + .replace(/^O K$/g, 'OK') + + const getErrorResponseSchema = (code: StatusCode, message: string, schema?: SchemaObject) => { + return { + type: 'object', + + properties: { + status: { + type: 'number', + min: 100, + title: 'the error status code', + example: code, + }, + message: { + type: 'string', + title: 'the error message', + example: message, + }, + data: schema, + traceparent: { + type: 'string', + title: 'w3c compliant unique traceparent for this request', + example: 'd5dbb17eec16e3c9fce9cf8adc766999', + externalDocs: 'https://www.w3.org/TR/trace-context/#traceparent-header-field-values', + }, + }, + required: ['status', 'message'], + } + } + + const findPathParamsRegex = /:[^:/]+/gm + for (const entry of this.routeDefinitions) { + const expose = entry.expose + const definition = expose.http + + let m: RegExpExecArray | null + + const routeParams: string[] = [] + while (true) { + m = findPathParamsRegex.exec(definition.path) + if (m === null) { + break + } + if (m.index === findPathParamsRegex.lastIndex) { + findPathParamsRegex.lastIndex++ + } + routeParams.push(...m.map(name => name)) + } + + const pathParams: ParameterObject[] = routeParams.map((pathParamName): ParameterObject => { + const name = pathParamName.replace('?', '').replace(':', '') + const required = !pathParamName.endsWith('?') + + const schema = expose.parameter?.properties?.[name] + + if (!schema) { + logger.warn( + `${definition.method} ${definition.path}: Path parameter ${name} is not in parameter schema and will not be available in service function`, + ) + } + + if (!!schema && isReferenceObject(schema)) { + return { + in: 'path', + name, + required, + ...schema, + } + } + + return { + in: 'path', + name, + required, + schema, + description: schema?.description ?? schema?.title, + } + }) + + const queryParams = + definition.openApi?.query?.map((queryParam): ParameterObject => { + const name = queryParam.name + const schema = expose.parameter?.properties?.[name] + const required = queryParam.required + + if (!schema) { + logger.warn( + `${definition.method} ${definition.path}: Query parameter ${name} is not in parameter schema and will not be available in service function`, + ) + } + + if (!!schema && isReferenceObject(schema)) { + return { + in: 'query', + name, + required, + ...schema, + } + } + + return { + in: 'query', + name, + required, + schema, + description: schema?.description ?? schema?.title, + } + }) ?? [] + + let path = definition.path + for (const pathParamName of routeParams) { + const name = pathParamName.replace('?', '').replace(':', '') + path = path.replace(pathParamName, `{${name}}`) + } + + let requestBody: RequestBodyObject | undefined + + if (['POST', 'PATCH', 'PUT'].includes(definition.method)) { + requestBody = { + content: { + [expose.contentTypeRequest ?? 'application/json']: { + schema: expose.inputPayload, + }, + }, + } + } + + const inputValidationFailed: SchemaObject = { + type: 'array', + items: { + type: 'object', + properties: { + validation: { + type: 'string', + example: 'invalid_string', + }, + code: { + type: 'string', + example: 'invalid_string', + }, + message: { + type: 'string', + example: 'String must contain at least 3 character(s)', + }, + expected: { + type: 'string', + example: 'string', + }, + received: { + type: 'string', + example: 'object', + }, + keys: { + type: 'array', + items: { + type: 'string', + }, + }, + minimum: { + type: 'number', + example: 3, + }, + maximum: { + type: 'number', + example: 32, + }, + path: { + type: 'array', + items: { + type: 'string', + example: 'username', + }, + }, + }, + required: ['message', 'code'], + }, + } + + const errorResponses: Record = {} + + if (expose.inputPayload) { + errorResponses[400] = { + description: getErrorName(400), + content: { + 'application/json': { + schema: getErrorResponseSchema(400, 'input validation failed', inputValidationFailed), + }, + }, + } + } + + if (expose.parameter && pathParams.length > 0) { + errorResponses[404] = { + description: getErrorName(404), + content: { + 'application/json': { + schema: getErrorResponseSchema(404, 'resource for given id does not exist'), + }, + }, + } + } + + if (securitySchema.length > 0 && definition.openApi?.isSecure) { + errorResponses[401] = { + description: getErrorName(401), + content: { + 'application/json': { + schema: getErrorResponseSchema(401, 'authentication required'), + }, + }, + } + } + + const addStatusCodes = (code: StatusCode) => { + errorResponses[code] = { + description: getErrorName(code), + content: { + 'application/json': { + schema: getErrorResponseSchema(code, getErrorName(code)), + }, + }, + } + } + + definition.openApi?.additionalStatusCodes + ?.filter(code => !Object.keys(errorResponses).includes(code.toString())) + .forEach(addStatusCodes) + + const traceIdParameter: ParameterObject = { + in: 'header', + required: false, + name: this.config.traceHeaderField ?? 'x-trace-id', + schema: { type: 'string' }, + example: '022bcd32-0a7c-4635-90ce-7940d0b9793f', + description: 'TraceID which can be used by business logic', + } + + const traceParent: ParameterObject = { + in: 'header', + required: false, + name: 'traceparent', + schema: { type: 'string' }, + description: 'see: https://www.w3.org/TR/trace-context/#traceparent-header-field-values', + } + + paths[path] = { + ...paths[path], + [definition.method.toLowerCase()]: { + deprecated: expose.deprecated, + security: securitySchema.length > 0 && definition.openApi?.isSecure ? securitySchema : [], + description: definition.openApi?.description, + summary: definition.openApi?.summary, + parameters: [...pathParams, ...queryParams, traceIdParameter, traceParent], + tags: definition.openApi?.tags, + operationId: definition.openApi?.operationId, + requestBody, + responses: { + [expose.outputPayload ? 200 : 204]: { + description: definition.openApi?.description, + content: { + [expose.contentTypeResponse ?? 'application/json']: { + schema: expose.outputPayload, + }, + }, + }, + ...errorResponses, + }, + }, + } + } + + if (isHealthzEnabled) { + if (!json.paths) { + json.paths = {} + } + json.paths['/healthz'] = { + get: { + description: 'indicates if the http server service is healthy', + responses: { + 200: { + description: 'http server service is up and running and successfully connected to event bridge', + content: { + 'application/json': { + schema: getErrorResponseSchema(200, getErrorName(200)), + }, + }, + }, + 500: { + description: 'http server service is not up and running or not successfully connected to event bridge', + content: { + 'application/json': { + schema: getErrorResponseSchema(500, getErrorName(500)), + }, + }, + }, + }, + }, + } + } + + reply.header('content-type', 'application/json; charset=utf-8') + return json + } + + return { + method: 'GET', + url, + handler, + } } diff --git a/packages/httpserver/src/service/httpServer/v1/subscription/serviceCommandsToRestApi/helper/addHeaders.impl.ts b/packages/httpserver/src/service/httpServer/v1/subscription/serviceCommandsToRestApi/helper/addHeaders.impl.ts index dc4394c59..9fd5a3acd 100644 --- a/packages/httpserver/src/service/httpServer/v1/subscription/serviceCommandsToRestApi/helper/addHeaders.impl.ts +++ b/packages/httpserver/src/service/httpServer/v1/subscription/serviceCommandsToRestApi/helper/addHeaders.impl.ts @@ -3,7 +3,9 @@ import { context, propagation, trace } from '@opentelemetry/api' import type { FastifyReply } from 'fastify' export const addHeaders = (span: Span, reply: FastifyReply) => { - const headers: Record = {} - propagation.inject(trace.setSpan(context.active(), span), headers) - Object.keys(headers).forEach((key) => reply.header(key, headers[key])) + const headers: Record = {} + propagation.inject(trace.setSpan(context.active(), span), headers) + for (const key in headers) { + reply.header(key, headers[key]) + } } diff --git a/packages/httpserver/src/service/httpServer/v1/subscription/serviceCommandsToRestApi/helper/addSpanTags.ts b/packages/httpserver/src/service/httpServer/v1/subscription/serviceCommandsToRestApi/helper/addSpanTags.ts index 6ba527947..91c2f0924 100644 --- a/packages/httpserver/src/service/httpServer/v1/subscription/serviceCommandsToRestApi/helper/addSpanTags.ts +++ b/packages/httpserver/src/service/httpServer/v1/subscription/serviceCommandsToRestApi/helper/addSpanTags.ts @@ -1,9 +1,9 @@ import type { Span } from '@opentelemetry/api' -import { SemanticAttributes } from '@opentelemetry/semantic-conventions' +import { ATTR_HTTP_HOST, ATTR_HTTP_METHOD, ATTR_URL_FULL } from '@opentelemetry/semantic-conventions/incubating' import type { FastifyRequest } from 'fastify' export const addSpanTags = (span: Span, request: FastifyRequest) => { - span.setAttribute(SemanticAttributes.HTTP_URL, request.url) - span.setAttribute(SemanticAttributes.HTTP_METHOD, request.method) - span.setAttribute(SemanticAttributes.HTTP_HOST, request.hostname) + span.setAttribute(ATTR_URL_FULL, request.url) + span.setAttribute(ATTR_HTTP_METHOD, request.method) + span.setAttribute(ATTR_HTTP_HOST, request.hostname) } diff --git a/packages/httpserver/src/service/httpServer/v1/subscription/serviceCommandsToRestApi/serviceCommandsToRestApi.test.ts b/packages/httpserver/src/service/httpServer/v1/subscription/serviceCommandsToRestApi/serviceCommandsToRestApi.test.ts index 0d9bc1d9c..8e59e7710 100644 --- a/packages/httpserver/src/service/httpServer/v1/subscription/serviceCommandsToRestApi/serviceCommandsToRestApi.test.ts +++ b/packages/httpserver/src/service/httpServer/v1/subscription/serviceCommandsToRestApi/serviceCommandsToRestApi.test.ts @@ -1,10 +1,4 @@ -import { - getCommandSuccessMessageMock, - getEventBridgeMock, - getLoggerMock, - getSubscriptionContextMock, - safeBind, -} from '@purista/core' +import { getCommandSuccessMessageMock, getEventBridgeMock, getLoggerMock, safeBind } from '@purista/core' import { createSandbox } from 'sinon' import { httpServerV1Service } from '../../httpServerV1Service.js' @@ -12,42 +6,46 @@ import { serviceCommandsToRestApiSubscriptionBuilder } from './serviceCommandsTo import type { HttpServerV1ServiceCommandsToRestApiInputPayload } from './types.js' describe('service HttpServer version 1 - subscription serviceCommandsToRestApi', () => { - let sandbox = createSandbox() - beforeEach(() => { - sandbox = createSandbox() - }) - - afterEach(() => { - sandbox.restore() - }) - - test('does not throw', async () => { - // create a service instance to be bind to the subscription function - const service = await httpServerV1Service.getInstance(getEventBridgeMock(sandbox).mock, { - logger: getLoggerMock(sandbox).mock, - }) - - // get the subscription function and bind to service instance to work properly - const serviceCommandsToRestApi = safeBind( - serviceCommandsToRestApiSubscriptionBuilder.getSubscriptionFunction(), - service, - ) - - // define the test input payload - const payload = undefined as unknown as Readonly - - // define the test input parameter - const parameter = undefined as unknown as Readonly - - // create a mock message with the expected input for the subscription function - const message = getCommandSuccessMessageMock(payload) - - // create a subscription context for the subscription function - const context = getSubscriptionContextMock(message, sandbox) - - // execute the subscription function - const result = await serviceCommandsToRestApi(context.mock, payload, parameter) - - expect(result).toBeUndefined() - }) + let sandbox = createSandbox() + beforeEach(() => { + sandbox = createSandbox() + }) + + afterEach(() => { + sandbox.restore() + }) + + test('does not throw', async () => { + // create a service instance to be bind to the subscription function + const service = await httpServerV1Service.getInstance(getEventBridgeMock(sandbox).mock, { + logger: getLoggerMock(sandbox).mock, + serviceConfig: { + port: 3000, + host: 'localhost', + }, + }) + + // get the subscription function and bind to service instance to work properly + const serviceCommandsToRestApi = safeBind( + serviceCommandsToRestApiSubscriptionBuilder.getSubscriptionFunction(), + service, + ) + + // define the test input payload + const payload = undefined as unknown as Readonly + + // define the test input parameter + const parameter = undefined as unknown as Readonly + + // create a mock message with the expected input for the subscription function + const message = getCommandSuccessMessageMock(payload) + + // create a subscription context for the subscription function + const context = serviceCommandsToRestApiSubscriptionBuilder.getSubscriptionContextMock({ message, sandbox }) + + // execute the subscription function + const result = await serviceCommandsToRestApi(context.mock, payload, parameter) + + expect(result).toBeUndefined() + }) }) diff --git a/packages/httpserver/src/service/httpServer/v1/subscription/serviceCommandsToRestApi/serviceCommandsToRestApiSubscriptionBuilder.ts b/packages/httpserver/src/service/httpServer/v1/subscription/serviceCommandsToRestApi/serviceCommandsToRestApiSubscriptionBuilder.ts index ed9c499b2..264917745 100644 --- a/packages/httpserver/src/service/httpServer/v1/subscription/serviceCommandsToRestApi/serviceCommandsToRestApiSubscriptionBuilder.ts +++ b/packages/httpserver/src/service/httpServer/v1/subscription/serviceCommandsToRestApi/serviceCommandsToRestApiSubscriptionBuilder.ts @@ -1,14 +1,14 @@ import { posix } from 'node:path' -import { context, propagation, SpanKind, SpanStatusCode } from '@opentelemetry/api' -import { SemanticAttributes } from '@opentelemetry/semantic-conventions' +import { SpanKind, SpanStatusCode, context, propagation } from '@opentelemetry/api' +import { ATTR_HTTP_RESPONSE_STATUS_CODE } from '@opentelemetry/semantic-conventions' import { - convertToSnakeCase, - EBMessageType, - HandledError, - isHttpExposedServiceMeta, - StatusCode, - UnhandledError, + EBMessageType, + HandledError, + StatusCode, + UnhandledError, + convertToSnakeCase, + isHttpExposedServiceMeta, } from '@purista/core' import type { FastifyReply, FastifyRequest } from 'fastify' import type { Methods } from 'trouter' @@ -17,140 +17,140 @@ import { httpServerV1ServiceBuilder } from '../../httpServerV1ServiceBuilder.js' import { addHeaders } from './helper/index.js' export const serviceCommandsToRestApiSubscriptionBuilder = httpServerV1ServiceBuilder - .getSubscriptionBuilder( - 'serviceCommandsToRestApi', - 'listens for InfoMessages and adds endpoints for commands if they are configured to be exposed as http endpoint', - ) - .adviceDurable(false) - .adviceAutoacknowledgeMessage() - .filterForMessageType(EBMessageType.InfoServiceFunctionAdded) - .receiveMessageOnEveryInstance() - .setSubscriptionFunction(async function ({ logger, message }, payload) { - if (!isHttpExposedServiceMeta(payload)) { - logger.debug('...skip exposing function') - return - } - - this.routeDefinitions.push(payload) - - const data = payload.expose - const version = message.sender.serviceVersion - const serviceName = message.sender.serviceName - const method = data.http.method - const apiMountPath = this.config.apiMountPath - const url = posix.join(apiMountPath ?? '/api', `v${version}`, data.http.path) - - data.http.path = url - - if (data.http.openApi) { - data.http.openApi.operationId = convertToSnakeCase(`${serviceName}_v${version}_${data.http.openApi.operationId}`) - } - - const contentType = data.contentTypeResponse ?? 'application/json' - const contentEncoding = data.contentEncodingResponse ?? 'utf-8' - - const getHandler = () => { - return async (request: FastifyRequest, reply: FastifyReply, parameter: Record) => { - const parentContext = propagation.extract(context.active(), request.headers) - - return this.startActiveSpan('handler', { kind: SpanKind.SERVER }, parentContext, async (span) => { - try { - addHeaders(span, reply) - const fastifyParams = request.params as Record - delete fastifyParams['*'] - - const queryParams: Record = {} - - // only allow defined query parameters and check if they are required - const queries = request.query as Record - if (data.http.openApi?.query) { - data.http.openApi.query.forEach((qp) => { - queryParams[qp.name] = queries[qp.name] - if (qp.required && !queries[qp.name]) { - throw new HandledError(StatusCode.BadRequest, `query parameter ${qp.name} is required`) - } - }) - } - - const parameterExtended = { - ...queryParams, - ...fastifyParams, - ...parameter, - } - - const principalId = request.principalId - const tenantId = request.tenantId - - let traceId: string | undefined - - const headerTraceId = request.headers[this.config.traceHeaderField] - if (Array.isArray(headerTraceId)) { - traceId = headerTraceId[0] - } else { - traceId = headerTraceId - } - - const response = await this.invoke( - { - traceId, - receiver: { - serviceName: message.sender.serviceName, - serviceVersion: message.sender.serviceVersion, - serviceTarget: message.sender.serviceTarget, - }, - payload: { payload: request.body, parameter: parameterExtended }, - principalId, - tenantId, - contentType, - contentEncoding, - }, - `${method}:${url}`, - ) - - const beforeResponse = this.beforeResponse.find(request.method as Methods, request.url) - - for (const hook of beforeResponse.handlers) { - await this.startActiveSpan('beforeResponseHook', { kind: SpanKind.SERVER }, undefined, async (_span) => { - hook(response, request, reply, beforeResponse.params) - }) - } - - reply.header('content-type', `${contentType}; charset=${contentEncoding}`) - if (response === undefined || response === '') { - span.setAttribute(SemanticAttributes.HTTP_STATUS_CODE, StatusCode.NoContent) - reply.statusCode = StatusCode.NoContent - } - - span.setAttribute(SemanticAttributes.HTTP_STATUS_CODE, StatusCode.OK) - - reply.send(response) - } catch (err) { - reply.header('content-type', 'application/json; charset=utf-8') - - if (err instanceof HandledError) { - reply.statusCode = err.errorCode - reply.send(err.getErrorResponse()) - return - } - const unhandledError = new UnhandledError() - - span.recordException(err as Error) - span.setStatus({ - code: SpanStatusCode.ERROR, - message: (err as Error).message, - }) - span.setAttribute(SemanticAttributes.HTTP_STATUS_CODE, unhandledError.errorCode) - - logger.error({ err, ...span.spanContext() }, 'unhandled error') - - reply.statusCode = unhandledError.errorCode - reply.send(unhandledError.getErrorResponse()) - } - }) - } - } - - this.routes.add(method, url, getHandler()) - - logger.debug({ method, url }, 'add handler') - }) + .getSubscriptionBuilder( + 'serviceCommandsToRestApi', + 'listens for InfoMessages and adds endpoints for commands if they are configured to be exposed as http endpoint', + ) + .adviceDurable(false) + .adviceAutoacknowledgeMessage() + .filterForMessageType(EBMessageType.InfoServiceFunctionAdded) + .receiveMessageOnEveryInstance() + .setSubscriptionFunction(async function ({ logger, message }, payload) { + if (!isHttpExposedServiceMeta(payload)) { + logger.debug('...skip exposing function') + return + } + + this.routeDefinitions.push(payload) + + const data = payload.expose + const version = message.sender.serviceVersion + const serviceName = message.sender.serviceName + const method = data.http.method + const apiMountPath = this.config.apiMountPath + const url = posix.join(apiMountPath ?? '/api', `v${version}`, data.http.path) + + data.http.path = url + + if (data.http.openApi) { + data.http.openApi.operationId = convertToSnakeCase(`${serviceName}_v${version}_${data.http.openApi.operationId}`) + } + + const contentType = data.contentTypeResponse ?? 'application/json' + const contentEncoding = data.contentEncodingResponse ?? 'utf-8' + + const getHandler = () => { + return async (request: FastifyRequest, reply: FastifyReply, parameter: Record) => { + const parentContext = propagation.extract(context.active(), request.headers) + + return this.startActiveSpan('handler', { kind: SpanKind.SERVER }, parentContext, async span => { + try { + addHeaders(span, reply) + const fastifyParams = request.params as Record + fastifyParams['*'] = undefined + + const queryParams: Record = {} + + // only allow defined query parameters and check if they are required + const queries = request.query as Record + if (data.http.openApi?.query) { + for (const qp of data.http.openApi.query) { + queryParams[qp.name] = queries[qp.name] + if (qp.required && !queries[qp.name]) { + throw new HandledError(StatusCode.BadRequest, `query parameter ${qp.name} is required`) + } + } + } + + const parameterExtended = { + ...queryParams, + ...fastifyParams, + ...parameter, + } + + const principalId = request.principalId + const tenantId = request.tenantId + + let traceId: string | undefined + + const headerTraceId = request.headers[this.config.traceHeaderField] + if (Array.isArray(headerTraceId)) { + traceId = headerTraceId[0] + } else { + traceId = headerTraceId + } + + const response = await this.invoke( + { + traceId, + receiver: { + serviceName: message.sender.serviceName, + serviceVersion: message.sender.serviceVersion, + serviceTarget: message.sender.serviceTarget, + }, + payload: { payload: request.body, parameter: parameterExtended }, + principalId, + tenantId, + contentType, + contentEncoding, + }, + `${method}:${url}`, + ) + + const beforeResponse = this.beforeResponse.find(request.method as Methods, request.url) + + for (const hook of beforeResponse.handlers) { + await this.startActiveSpan('beforeResponseHook', { kind: SpanKind.SERVER }, undefined, async _span => { + hook(response, request, reply, beforeResponse.params) + }) + } + + reply.header('content-type', `${contentType}; charset=${contentEncoding}`) + if (response === undefined || response === '') { + span.setAttribute(ATTR_HTTP_RESPONSE_STATUS_CODE, StatusCode.NoContent) + reply.statusCode = StatusCode.NoContent + } + + span.setAttribute(ATTR_HTTP_RESPONSE_STATUS_CODE, StatusCode.OK) + + reply.send(response) + } catch (err) { + reply.header('content-type', 'application/json; charset=utf-8') + + if (err instanceof HandledError) { + reply.statusCode = err.errorCode + reply.send(err.getErrorResponse()) + return + } + const unhandledError = new UnhandledError() + + span.recordException(err as Error) + span.setStatus({ + code: SpanStatusCode.ERROR, + message: (err as Error).message, + }) + span.setAttribute(ATTR_HTTP_RESPONSE_STATUS_CODE, unhandledError.errorCode) + + logger.error({ err, ...span.spanContext() }, 'unhandled error') + + reply.statusCode = unhandledError.errorCode + reply.send(unhandledError.getErrorResponse()) + } + }) + } + } + + this.routes.add(method, url, getHandler()) + + logger.debug({ method, url }, 'add handler') + }) diff --git a/packages/httpserver/src/service/httpServer/v1/subscription/serviceCommandsToRestApi/types.ts b/packages/httpserver/src/service/httpServer/v1/subscription/serviceCommandsToRestApi/types.ts index 06e1113ad..de1cf4035 100644 --- a/packages/httpserver/src/service/httpServer/v1/subscription/serviceCommandsToRestApi/types.ts +++ b/packages/httpserver/src/service/httpServer/v1/subscription/serviceCommandsToRestApi/types.ts @@ -3,5 +3,5 @@ import type { z } from 'zod' import type { httpServerV1ServiceCommandsToRestApiInputPayloadSchema } from './schema.js' export type HttpServerV1ServiceCommandsToRestApiInputPayload = z.output< - typeof httpServerV1ServiceCommandsToRestApiInputPayloadSchema + typeof httpServerV1ServiceCommandsToRestApiInputPayloadSchema > diff --git a/packages/httpserver/src/service/httpServer/v1/types/BeforeResponseHook.ts b/packages/httpserver/src/service/httpServer/v1/types/BeforeResponseHook.ts index 06337965b..de199a18c 100644 --- a/packages/httpserver/src/service/httpServer/v1/types/BeforeResponseHook.ts +++ b/packages/httpserver/src/service/httpServer/v1/types/BeforeResponseHook.ts @@ -1,8 +1,8 @@ import type { FastifyReply, FastifyRequest } from 'fastify' export type BeforeResponseHook = ( - payload: T, - request: FastifyRequest, - reply: FastifyReply, - parameter: Record, + payload: T, + request: FastifyRequest, + reply: FastifyReply, + parameter: Record, ) => void diff --git a/packages/httpserver/test/integration.test.ts b/packages/httpserver/test/integration.test.ts index 361e124e1..92163169b 100644 --- a/packages/httpserver/test/integration.test.ts +++ b/packages/httpserver/test/integration.test.ts @@ -1,5 +1,5 @@ import type { EventBridge, Service } from '@purista/core' -import { DefaultEventBridge, getLoggerMock, HttpClient } from '@purista/core' +import { DefaultEventBridge, HttpClient, getLoggerMock } from '@purista/core' import type { OpenAPIObject } from 'openapi3-ts/oas31' import { httpServerV1Service } from '../src/index.js' @@ -7,126 +7,126 @@ import { getIndexHtml, getJsInit } from '../src/service/httpServer/v1/routes/ind import { theServiceV1Service } from './service/theService/v1/index.js' describe('httpserver integration test', () => { - let eventBridge: EventBridge - let server: Service - const port = 8083 - const client = new HttpClient({ baseUrl: `http://127.0.0.1:${port}`, logger: getLoggerMock().mock }) - - const content = JSON.stringify({ some: 'content' }) - - const apiMountPath = '/api' - - const config = { - logger: getLoggerMock().mock, - serviceConfig: { - port, - host: '127.0.0.1', - enableHealthz: true, - enableCors: false, - enableHelmet: false, - apiMountPath, - openApi: { - enabled: true, - info: { - title: 'backend api', - description: 'OpenApi definition for server endpoints', - version: '1.0.0', - }, - }, - }, - } - - beforeAll(async () => { - eventBridge = new DefaultEventBridge({ logger: getLoggerMock().mock }) - await eventBridge.start() - - server = await httpServerV1Service.getInstance(eventBridge, config) - await server.start() - await new Promise((resolve) => setTimeout(resolve, 0)) - }) - - afterAll(async () => { - await server.destroy() - await eventBridge.destroy() - }) - - it('returns healthz', async () => { - await expect(client.get('/healthz')).resolves.toEqual({ - status: 200, - message: 'OK', - }) - }) - - it('returns /api', async () => { - await expect(client.get(apiMountPath)).resolves.toEqual(getIndexHtml(apiMountPath)) - }) - - it('returns /api/initializer.js', async () => { - await expect(client.get(apiMountPath + '/initializer.js')).resolves.toEqual(getJsInit(apiMountPath)) - }) - - it('returns /api/openapi.json', async () => { - const response = await client.get(apiMountPath + '/openapi.json') - expect(response.info.description).toEqual(config.serviceConfig.openApi.info.description) - expect(response.info.title).toEqual(config.serviceConfig.openApi.info.title) - expect(response.info.version).toEqual(config.serviceConfig.openApi.info.version) - expect(response.paths?.['/healthz']).toBeDefined() - }) - - describe('with services', () => { - beforeAll(async () => { - // set up the service - const theService = await theServiceV1Service.getInstance(eventBridge, { logger: getLoggerMock().mock }) - await theService.start() - - await new Promise((resolve) => setTimeout(resolve, 5000)) - }) - - it('returns ' + apiMountPath + '/openapi.json', async () => { - const response = await client.get(apiMountPath + '/openapi.json') - - expect(response.paths?.['/healthz']).toBeDefined() - expect(response.paths?.[apiMountPath + '/v1/ping']).toBeDefined() - expect(response.paths?.[apiMountPath + '/v1/unknown']).toBeUndefined() - expect(response.paths?.[apiMountPath + '/v1/error']).toBeDefined() - expect(response.paths?.[apiMountPath + '/v1/post']).toBeDefined() - expect(response.paths?.[apiMountPath + '/v1/patch']).toBeDefined() - expect(response.paths?.[apiMountPath + '/v1/put']).toBeDefined() - expect(response.paths?.[apiMountPath + '/v1/delete']).toBeDefined() - }) - - it('exposes http get endpoint', async () => { - await expect(client.get(apiMountPath + '/v1/ping', { query: { required: 'true' } })).resolves.toEqual({ - ping: true, - }) - }) - - it('returns a error on invalid query parameter', async () => { - await expect(client.get(apiMountPath + '/v1/ping')).rejects.toEqual(new Error('Bad Request')) - }) - - it('has a 404 handling', async () => { - await expect(client.get(apiMountPath + '/v1/unknown')).rejects.toEqual(new Error('Not Found')) - }) - - it('returns a error if command returns error', async () => { - await expect(client.get(apiMountPath + '/v1/error')).rejects.toEqual(new Error('Internal Server Error')) - }) - - it('exposes http post endpoint', async () => { - await expect(client.post(apiMountPath + '/v1/post', content)).resolves.toEqual({ payload: content }) - }) - - it('exposes http patch endpoint', async () => { - await expect(client.patch(apiMountPath + '/v1/patch', content)).resolves.toEqual({ payload: content }) - }) - - it('exposes http put endpoint', async () => { - await expect(client.put(apiMountPath + '/v1/put', content)).resolves.toEqual({ payload: content }) - }) - - it('exposes http delete endpoint', async () => { - await expect(client.delete(apiMountPath + '/v1/delete')).resolves.toBeUndefined() - }) - }) + let eventBridge: EventBridge + let server: Service + const port = 8083 + const client = new HttpClient({ baseUrl: `http://127.0.0.1:${port}`, logger: getLoggerMock().mock }) + + const content = JSON.stringify({ some: 'content' }) + + const apiMountPath = '/api' + + const config = { + logger: getLoggerMock().mock, + serviceConfig: { + port, + host: '127.0.0.1', + enableHealthz: true, + enableCors: false, + enableHelmet: false, + apiMountPath, + openApi: { + enabled: true, + info: { + title: 'backend api', + description: 'OpenApi definition for server endpoints', + version: '1.0.0', + }, + }, + }, + } + + beforeAll(async () => { + eventBridge = new DefaultEventBridge({ logger: getLoggerMock().mock }) + await eventBridge.start() + + server = await httpServerV1Service.getInstance(eventBridge, config) + await server.start() + await new Promise(resolve => setTimeout(resolve, 0)) + }) + + afterAll(async () => { + await server.destroy() + await eventBridge.destroy() + }) + + it('returns healthz', async () => { + await expect(client.get('/healthz')).resolves.toEqual({ + status: 200, + message: 'OK', + }) + }) + + it('returns /api', async () => { + await expect(client.get(apiMountPath)).resolves.toEqual(getIndexHtml(apiMountPath)) + }) + + it('returns /api/initializer.js', async () => { + await expect(client.get(`${apiMountPath}/initializer.js`)).resolves.toEqual(getJsInit(apiMountPath)) + }) + + it('returns /api/openapi.json', async () => { + const response = await client.get(`${apiMountPath}/openapi.json`) + expect(response.info.description).toEqual(config.serviceConfig.openApi.info.description) + expect(response.info.title).toEqual(config.serviceConfig.openApi.info.title) + expect(response.info.version).toEqual(config.serviceConfig.openApi.info.version) + expect(response.paths?.['/healthz']).toBeDefined() + }) + + describe('with services', () => { + beforeAll(async () => { + // set up the service + const theService = await theServiceV1Service.getInstance(eventBridge, { logger: getLoggerMock().mock }) + await theService.start() + + await new Promise(resolve => setTimeout(resolve, 5000)) + }) + + it(`returns ${apiMountPath}/openapi.json`, async () => { + const response = await client.get(`${apiMountPath}/openapi.json`) + + expect(response.paths?.['/healthz']).toBeDefined() + expect(response.paths?.[`${apiMountPath}/v1/ping`]).toBeDefined() + expect(response.paths?.[`${apiMountPath}/v1/unknown`]).toBeUndefined() + expect(response.paths?.[`${apiMountPath}/v1/error`]).toBeDefined() + expect(response.paths?.[`${apiMountPath}/v1/post`]).toBeDefined() + expect(response.paths?.[`${apiMountPath}/v1/patch`]).toBeDefined() + expect(response.paths?.[`${apiMountPath}/v1/put`]).toBeDefined() + expect(response.paths?.[`${apiMountPath}/v1/delete`]).toBeDefined() + }) + + it('exposes http get endpoint', async () => { + await expect(client.get(`${apiMountPath}/v1/ping`, { query: { required: 'true' } })).resolves.toEqual({ + ping: true, + }) + }) + + it('returns a error on invalid query parameter', async () => { + await expect(client.get(`${apiMountPath}/v1/ping`)).rejects.toEqual(new Error('Bad Request')) + }) + + it('has a 404 handling', async () => { + await expect(client.get(`${apiMountPath}/v1/unknown`)).rejects.toEqual(new Error('Not Found')) + }) + + it('returns a error if command returns error', async () => { + await expect(client.get(`${apiMountPath}/v1/error`)).rejects.toEqual(new Error('Internal Server Error')) + }) + + it('exposes http post endpoint', async () => { + await expect(client.post(`${apiMountPath}/v1/post`, content)).resolves.toEqual({ payload: content }) + }) + + it('exposes http patch endpoint', async () => { + await expect(client.patch(`${apiMountPath}/v1/patch`, content)).resolves.toEqual({ payload: content }) + }) + + it('exposes http put endpoint', async () => { + await expect(client.put(`${apiMountPath}/v1/put`, content)).resolves.toEqual({ payload: content }) + }) + + it('exposes http delete endpoint', async () => { + await expect(client.delete(`${apiMountPath}/v1/delete`)).resolves.toBeUndefined() + }) + }) }) diff --git a/packages/httpserver/test/service/theService/generalTheServiceServiceInfo.ts b/packages/httpserver/test/service/theService/generalTheServiceServiceInfo.ts index 45737f772..d4dc4a13a 100644 --- a/packages/httpserver/test/service/theService/generalTheServiceServiceInfo.ts +++ b/packages/httpserver/test/service/theService/generalTheServiceServiceInfo.ts @@ -1,6 +1,6 @@ import type { ServiceInfoType } from '@purista/core' export const generalTheServiceServiceInfo: Omit = { - serviceName: 'TheService', - serviceDescription: 'a example service', + serviceName: 'TheService', + serviceDescription: 'a example service', } diff --git a/packages/httpserver/test/service/theService/v1/command/delete/deleteCommandBuilder.ts b/packages/httpserver/test/service/theService/v1/command/delete/deleteCommandBuilder.ts index 073757a35..3c9e39068 100644 --- a/packages/httpserver/test/service/theService/v1/command/delete/deleteCommandBuilder.ts +++ b/packages/httpserver/test/service/theService/v1/command/delete/deleteCommandBuilder.ts @@ -1,14 +1,14 @@ import { theServiceServiceBuilder } from '../../theServiceServiceBuilder.js' import { - theServiceV1DeleteInputParameterSchema, - theServiceV1DeleteInputPayloadSchema, - theServiceV1DeleteOutputPayloadSchema, + theServiceV1DeleteInputParameterSchema, + theServiceV1DeleteInputPayloadSchema, + theServiceV1DeleteOutputPayloadSchema, } from './schema.js' export const deleteCommandBuilder = theServiceServiceBuilder - .getCommandBuilder('delete', 'provide a dummy command') - .addPayloadSchema(theServiceV1DeleteInputPayloadSchema) - .addParameterSchema(theServiceV1DeleteInputParameterSchema) - .addOutputSchema(theServiceV1DeleteOutputPayloadSchema) - .exposeAsHttpEndpoint('DELETE', 'delete') - .setCommandFunction(async function (_context, _payload, _parameter) {}) + .getCommandBuilder('delete', 'provide a dummy command') + .addPayloadSchema(theServiceV1DeleteInputPayloadSchema) + .addParameterSchema(theServiceV1DeleteInputParameterSchema) + .addOutputSchema(theServiceV1DeleteOutputPayloadSchema) + .exposeAsHttpEndpoint('DELETE', 'delete') + .setCommandFunction(async function (_context, _payload, _parameter) {}) diff --git a/packages/httpserver/test/service/theService/v1/command/delete/schema.ts b/packages/httpserver/test/service/theService/v1/command/delete/schema.ts index 98a542b73..0fd7a0ae9 100644 --- a/packages/httpserver/test/service/theService/v1/command/delete/schema.ts +++ b/packages/httpserver/test/service/theService/v1/command/delete/schema.ts @@ -3,7 +3,7 @@ import { z } from 'zod' // define the input parameters export const theServiceV1DeleteInputParameterSchema = extendApi(z.object({}), { - title: 'delete input parameter schema', + title: 'delete input parameter schema', }) // define the input payload @@ -11,5 +11,5 @@ export const theServiceV1DeleteInputPayloadSchema = extendApi(z.any(), { title: // define the output payload export const theServiceV1DeleteOutputPayloadSchema = extendApi(z.void(), { - title: 'put output payload schema', + title: 'put output payload schema', }) diff --git a/packages/httpserver/test/service/theService/v1/command/delete/types.ts b/packages/httpserver/test/service/theService/v1/command/delete/types.ts index f6d611a99..61a4bfdc9 100644 --- a/packages/httpserver/test/service/theService/v1/command/delete/types.ts +++ b/packages/httpserver/test/service/theService/v1/command/delete/types.ts @@ -1,9 +1,9 @@ import type { z } from 'zod' import type { - theServiceV1DeleteInputParameterSchema, - theServiceV1DeleteInputPayloadSchema, - theServiceV1DeleteOutputPayloadSchema, + theServiceV1DeleteInputParameterSchema, + theServiceV1DeleteInputPayloadSchema, + theServiceV1DeleteOutputPayloadSchema, } from './schema.js' export type TheServiceV1DeleteInputParameter = z.input diff --git a/packages/httpserver/test/service/theService/v1/command/error/errorCommandBuilder.ts b/packages/httpserver/test/service/theService/v1/command/error/errorCommandBuilder.ts index 6a86f1f32..16c5ff1da 100644 --- a/packages/httpserver/test/service/theService/v1/command/error/errorCommandBuilder.ts +++ b/packages/httpserver/test/service/theService/v1/command/error/errorCommandBuilder.ts @@ -1,16 +1,16 @@ import { theServiceServiceBuilder } from '../../theServiceServiceBuilder.js' import { - theServiceV1ErrorInputParameterSchema, - theServiceV1ErrorInputPayloadSchema, - theServiceV1ErrorOutputPayloadSchema, + theServiceV1ErrorInputParameterSchema, + theServiceV1ErrorInputPayloadSchema, + theServiceV1ErrorOutputPayloadSchema, } from './schema.js' export const errorCommandBuilder = theServiceServiceBuilder - .getCommandBuilder('error', 'provide a dummy command') - .addPayloadSchema(theServiceV1ErrorInputPayloadSchema) - .addParameterSchema(theServiceV1ErrorInputParameterSchema) - .addOutputSchema(theServiceV1ErrorOutputPayloadSchema) - .exposeAsHttpEndpoint('GET', 'error') - .setCommandFunction(async function (_context, _payload, _parameter) { - throw new Error('some error') - }) + .getCommandBuilder('error', 'provide a dummy command') + .addPayloadSchema(theServiceV1ErrorInputPayloadSchema) + .addParameterSchema(theServiceV1ErrorInputParameterSchema) + .addOutputSchema(theServiceV1ErrorOutputPayloadSchema) + .exposeAsHttpEndpoint('GET', 'error') + .setCommandFunction(async function (_context, _payload, _parameter) { + throw new Error('some error') + }) diff --git a/packages/httpserver/test/service/theService/v1/command/error/schema.ts b/packages/httpserver/test/service/theService/v1/command/error/schema.ts index 463b3a027..a1cb48dd6 100644 --- a/packages/httpserver/test/service/theService/v1/command/error/schema.ts +++ b/packages/httpserver/test/service/theService/v1/command/error/schema.ts @@ -9,5 +9,5 @@ export const theServiceV1ErrorInputPayloadSchema = extendApi(z.undefined(), { ti // define the output payload export const theServiceV1ErrorOutputPayloadSchema = extendApi(z.object({ error: z.boolean() }), { - title: 'error output payload schema', + title: 'error output payload schema', }) diff --git a/packages/httpserver/test/service/theService/v1/command/error/types.ts b/packages/httpserver/test/service/theService/v1/command/error/types.ts index 2dfd509ea..0981793e8 100644 --- a/packages/httpserver/test/service/theService/v1/command/error/types.ts +++ b/packages/httpserver/test/service/theService/v1/command/error/types.ts @@ -1,9 +1,9 @@ import type { z } from 'zod' import type { - theServiceV1ErrorInputParameterSchema, - theServiceV1ErrorInputPayloadSchema, - theServiceV1ErrorOutputPayloadSchema, + theServiceV1ErrorInputParameterSchema, + theServiceV1ErrorInputPayloadSchema, + theServiceV1ErrorOutputPayloadSchema, } from './schema.js' export type TheServiceV1ErrorInputParameter = z.input diff --git a/packages/httpserver/test/service/theService/v1/command/patch/patchCommandBuilder.ts b/packages/httpserver/test/service/theService/v1/command/patch/patchCommandBuilder.ts index 271a8a269..21ccce6de 100644 --- a/packages/httpserver/test/service/theService/v1/command/patch/patchCommandBuilder.ts +++ b/packages/httpserver/test/service/theService/v1/command/patch/patchCommandBuilder.ts @@ -1,18 +1,18 @@ import { theServiceServiceBuilder } from '../../theServiceServiceBuilder.js' import { - theServiceV1PatchInputParameterSchema, - theServiceV1PatchInputPayloadSchema, - theServiceV1PatchOutputPayloadSchema, + theServiceV1PatchInputParameterSchema, + theServiceV1PatchInputPayloadSchema, + theServiceV1PatchOutputPayloadSchema, } from './schema.js' export const patchCommandBuilder = theServiceServiceBuilder - .getCommandBuilder('patch', 'provide a dummy command') - .addPayloadSchema(theServiceV1PatchInputPayloadSchema) - .addParameterSchema(theServiceV1PatchInputParameterSchema) - .addOutputSchema(theServiceV1PatchOutputPayloadSchema) - .exposeAsHttpEndpoint('PATCH', 'patch') - .setCommandFunction(async function (_context, payload, _parameter) { - return { - payload, - } - }) + .getCommandBuilder('patch', 'provide a dummy command') + .addPayloadSchema(theServiceV1PatchInputPayloadSchema) + .addParameterSchema(theServiceV1PatchInputParameterSchema) + .addOutputSchema(theServiceV1PatchOutputPayloadSchema) + .exposeAsHttpEndpoint('PATCH', 'patch') + .setCommandFunction(async function (_context, payload, _parameter) { + return { + payload, + } + }) diff --git a/packages/httpserver/test/service/theService/v1/command/patch/schema.ts b/packages/httpserver/test/service/theService/v1/command/patch/schema.ts index cbf9ec2bb..368d58059 100644 --- a/packages/httpserver/test/service/theService/v1/command/patch/schema.ts +++ b/packages/httpserver/test/service/theService/v1/command/patch/schema.ts @@ -9,5 +9,5 @@ export const theServiceV1PatchInputPayloadSchema = extendApi(z.any(), { title: ' // define the output payload export const theServiceV1PatchOutputPayloadSchema = extendApi(z.any(), { - title: 'patch output payload schema', + title: 'patch output payload schema', }) diff --git a/packages/httpserver/test/service/theService/v1/command/patch/types.ts b/packages/httpserver/test/service/theService/v1/command/patch/types.ts index bdaa86252..cfbd5f28d 100644 --- a/packages/httpserver/test/service/theService/v1/command/patch/types.ts +++ b/packages/httpserver/test/service/theService/v1/command/patch/types.ts @@ -1,9 +1,9 @@ import type { z } from 'zod' import type { - theServiceV1PatchInputParameterSchema, - theServiceV1PatchInputPayloadSchema, - theServiceV1PatchOutputPayloadSchema, + theServiceV1PatchInputParameterSchema, + theServiceV1PatchInputPayloadSchema, + theServiceV1PatchOutputPayloadSchema, } from './schema.js' export type TheServiceV1PatchInputParameter = z.input diff --git a/packages/httpserver/test/service/theService/v1/command/ping/pingCommandBuilder.ts b/packages/httpserver/test/service/theService/v1/command/ping/pingCommandBuilder.ts index d40d31eb9..f16f2c7bc 100644 --- a/packages/httpserver/test/service/theService/v1/command/ping/pingCommandBuilder.ts +++ b/packages/httpserver/test/service/theService/v1/command/ping/pingCommandBuilder.ts @@ -1,19 +1,19 @@ import { theServiceServiceBuilder } from '../../theServiceServiceBuilder.js' import { - theServiceV1PingInputParameterSchema, - theServiceV1PingInputPayloadSchema, - theServiceV1PingOutputPayloadSchema, + theServiceV1PingInputParameterSchema, + theServiceV1PingInputPayloadSchema, + theServiceV1PingOutputPayloadSchema, } from './schema.js' export const pingCommandBuilder = theServiceServiceBuilder - .getCommandBuilder('ping', 'provide a dummy command') - .addPayloadSchema(theServiceV1PingInputPayloadSchema) - .addParameterSchema(theServiceV1PingInputParameterSchema) - .addOutputSchema(theServiceV1PingOutputPayloadSchema) - .exposeAsHttpEndpoint('GET', 'ping') - .addQueryParameters({ name: 'param', required: false }, { required: true, name: 'required' }) - .setCommandFunction(async function (_context, _payload, _parameter) { - return { - ping: true, - } - }) + .getCommandBuilder('ping', 'provide a dummy command') + .addPayloadSchema(theServiceV1PingInputPayloadSchema) + .addParameterSchema(theServiceV1PingInputParameterSchema) + .addOutputSchema(theServiceV1PingOutputPayloadSchema) + .exposeAsHttpEndpoint('GET', 'ping') + .addQueryParameters({ name: 'param', required: false }, { required: true, name: 'required' }) + .setCommandFunction(async function (_context, _payload, _parameter) { + return { + ping: true, + } + }) diff --git a/packages/httpserver/test/service/theService/v1/command/ping/schema.ts b/packages/httpserver/test/service/theService/v1/command/ping/schema.ts index b20b855b7..a49cf1bdd 100644 --- a/packages/httpserver/test/service/theService/v1/command/ping/schema.ts +++ b/packages/httpserver/test/service/theService/v1/command/ping/schema.ts @@ -3,11 +3,11 @@ import { z } from 'zod' // define the input parameters export const theServiceV1PingInputParameterSchema = extendApi( - z.object({ - param: z.string().optional(), - required: z.string(), - }), - { title: 'ping input parameter schema' }, + z.object({ + param: z.string().optional(), + required: z.string(), + }), + { title: 'ping input parameter schema' }, ) // define the input payload @@ -15,5 +15,5 @@ export const theServiceV1PingInputPayloadSchema = extendApi(z.undefined(), { tit // define the output payload export const theServiceV1PingOutputPayloadSchema = extendApi(z.object({ ping: z.boolean() }), { - title: 'ping output payload schema', + title: 'ping output payload schema', }) diff --git a/packages/httpserver/test/service/theService/v1/command/ping/types.ts b/packages/httpserver/test/service/theService/v1/command/ping/types.ts index babb58f4b..1aacbf07a 100644 --- a/packages/httpserver/test/service/theService/v1/command/ping/types.ts +++ b/packages/httpserver/test/service/theService/v1/command/ping/types.ts @@ -1,9 +1,9 @@ import type { z } from 'zod' import type { - theServiceV1PingInputParameterSchema, - theServiceV1PingInputPayloadSchema, - theServiceV1PingOutputPayloadSchema, + theServiceV1PingInputParameterSchema, + theServiceV1PingInputPayloadSchema, + theServiceV1PingOutputPayloadSchema, } from './schema.js' export type TheServiceV1PingInputParameter = z.input diff --git a/packages/httpserver/test/service/theService/v1/command/post/postCommandBuilder.ts b/packages/httpserver/test/service/theService/v1/command/post/postCommandBuilder.ts index c4fab4486..f282b53a2 100644 --- a/packages/httpserver/test/service/theService/v1/command/post/postCommandBuilder.ts +++ b/packages/httpserver/test/service/theService/v1/command/post/postCommandBuilder.ts @@ -1,18 +1,18 @@ import { theServiceServiceBuilder } from '../../theServiceServiceBuilder.js' import { - theServiceV1PostInputParameterSchema, - theServiceV1PostInputPayloadSchema, - theServiceV1PostOutputPayloadSchema, + theServiceV1PostInputParameterSchema, + theServiceV1PostInputPayloadSchema, + theServiceV1PostOutputPayloadSchema, } from './schema.js' export const postCommandBuilder = theServiceServiceBuilder - .getCommandBuilder('post', 'provide a dummy command') - .addPayloadSchema(theServiceV1PostInputPayloadSchema) - .addParameterSchema(theServiceV1PostInputParameterSchema) - .addOutputSchema(theServiceV1PostOutputPayloadSchema) - .exposeAsHttpEndpoint('POST', 'post') - .setCommandFunction(async function (_context, payload, _parameter) { - return { - payload, - } - }) + .getCommandBuilder('post', 'provide a dummy command') + .addPayloadSchema(theServiceV1PostInputPayloadSchema) + .addParameterSchema(theServiceV1PostInputParameterSchema) + .addOutputSchema(theServiceV1PostOutputPayloadSchema) + .exposeAsHttpEndpoint('POST', 'post') + .setCommandFunction(async function (_context, payload, _parameter) { + return { + payload, + } + }) diff --git a/packages/httpserver/test/service/theService/v1/command/post/schema.ts b/packages/httpserver/test/service/theService/v1/command/post/schema.ts index a03b3526d..2c2ed82bd 100644 --- a/packages/httpserver/test/service/theService/v1/command/post/schema.ts +++ b/packages/httpserver/test/service/theService/v1/command/post/schema.ts @@ -9,5 +9,5 @@ export const theServiceV1PostInputPayloadSchema = extendApi(z.any(), { title: 'p // define the output payload export const theServiceV1PostOutputPayloadSchema = extendApi(z.any(), { - title: 'post output payload schema', + title: 'post output payload schema', }) diff --git a/packages/httpserver/test/service/theService/v1/command/post/types.ts b/packages/httpserver/test/service/theService/v1/command/post/types.ts index 862670f8e..7f78d4ad7 100644 --- a/packages/httpserver/test/service/theService/v1/command/post/types.ts +++ b/packages/httpserver/test/service/theService/v1/command/post/types.ts @@ -1,9 +1,9 @@ import type { z } from 'zod' import type { - theServiceV1PostInputParameterSchema, - theServiceV1PostInputPayloadSchema, - theServiceV1PostOutputPayloadSchema, + theServiceV1PostInputParameterSchema, + theServiceV1PostInputPayloadSchema, + theServiceV1PostOutputPayloadSchema, } from './schema.js' export type TheServiceV1PostInputParameter = z.input diff --git a/packages/httpserver/test/service/theService/v1/command/put/putCommandBuilder.ts b/packages/httpserver/test/service/theService/v1/command/put/putCommandBuilder.ts index bb1556790..7595098c1 100644 --- a/packages/httpserver/test/service/theService/v1/command/put/putCommandBuilder.ts +++ b/packages/httpserver/test/service/theService/v1/command/put/putCommandBuilder.ts @@ -1,18 +1,18 @@ import { theServiceServiceBuilder } from '../../theServiceServiceBuilder.js' import { - theServiceV1PutInputParameterSchema, - theServiceV1PutInputPayloadSchema, - theServiceV1PutOutputPayloadSchema, + theServiceV1PutInputParameterSchema, + theServiceV1PutInputPayloadSchema, + theServiceV1PutOutputPayloadSchema, } from './schema.js' export const putCommandBuilder = theServiceServiceBuilder - .getCommandBuilder('put', 'provide a dummy command') - .addPayloadSchema(theServiceV1PutInputPayloadSchema) - .addParameterSchema(theServiceV1PutInputParameterSchema) - .addOutputSchema(theServiceV1PutOutputPayloadSchema) - .exposeAsHttpEndpoint('PUT', 'put') - .setCommandFunction(async function (_context, payload, _parameter) { - return { - payload, - } - }) + .getCommandBuilder('put', 'provide a dummy command') + .addPayloadSchema(theServiceV1PutInputPayloadSchema) + .addParameterSchema(theServiceV1PutInputParameterSchema) + .addOutputSchema(theServiceV1PutOutputPayloadSchema) + .exposeAsHttpEndpoint('PUT', 'put') + .setCommandFunction(async function (_context, payload, _parameter) { + return { + payload, + } + }) diff --git a/packages/httpserver/test/service/theService/v1/command/put/schema.ts b/packages/httpserver/test/service/theService/v1/command/put/schema.ts index 77cb18c67..661fea101 100644 --- a/packages/httpserver/test/service/theService/v1/command/put/schema.ts +++ b/packages/httpserver/test/service/theService/v1/command/put/schema.ts @@ -9,5 +9,5 @@ export const theServiceV1PutInputPayloadSchema = extendApi(z.any(), { title: 'pu // define the output payload export const theServiceV1PutOutputPayloadSchema = extendApi(z.any(), { - title: 'put output payload schema', + title: 'put output payload schema', }) diff --git a/packages/httpserver/test/service/theService/v1/command/put/types.ts b/packages/httpserver/test/service/theService/v1/command/put/types.ts index bbb0c339c..5414c02a1 100644 --- a/packages/httpserver/test/service/theService/v1/command/put/types.ts +++ b/packages/httpserver/test/service/theService/v1/command/put/types.ts @@ -1,9 +1,9 @@ import type { z } from 'zod' import type { - theServiceV1PutInputParameterSchema, - theServiceV1PutInputPayloadSchema, - theServiceV1PutOutputPayloadSchema, + theServiceV1PutInputParameterSchema, + theServiceV1PutInputPayloadSchema, + theServiceV1PutOutputPayloadSchema, } from './schema.js' export type TheServiceV1PutInputParameter = z.input diff --git a/packages/httpserver/test/service/theService/v1/theServiceServiceBuilder.ts b/packages/httpserver/test/service/theService/v1/theServiceServiceBuilder.ts index 2b393ff0c..2f61f4814 100644 --- a/packages/httpserver/test/service/theService/v1/theServiceServiceBuilder.ts +++ b/packages/httpserver/test/service/theService/v1/theServiceServiceBuilder.ts @@ -5,12 +5,12 @@ import { generalTheServiceServiceInfo } from '../generalTheServiceServiceInfo.js import { theServiceServiceV1ConfigSchema } from './theServiceServiceConfig.js' export const theServiceServiceInfo: ServiceInfoType = { - serviceVersion: '1', - ...generalTheServiceServiceInfo, + serviceVersion: '1', + ...generalTheServiceServiceInfo, } // create a service builder instance and assign service config schema and default config. -export const theServiceServiceBuilder = new ServiceBuilder(theServiceServiceInfo) - .setConfigSchema(theServiceServiceV1ConfigSchema) - .setDefaultConfig({}) +export const theServiceServiceBuilder = new ServiceBuilder(theServiceServiceInfo).setConfigSchema( + theServiceServiceV1ConfigSchema, +) diff --git a/packages/httpserver/test/service/theService/v1/theServiceV1Service.ts b/packages/httpserver/test/service/theService/v1/theServiceV1Service.ts index a937ad836..128a373d5 100644 --- a/packages/httpserver/test/service/theService/v1/theServiceV1Service.ts +++ b/packages/httpserver/test/service/theService/v1/theServiceV1Service.ts @@ -13,16 +13,16 @@ import { theServiceServiceBuilder } from './theServiceServiceBuilder.js' // other service config should be done in ./theServiceServiceBuilder.ts file const commandDefinitions: CommandDefinitionList = [ - pingCommandBuilder.getDefinition(), - postCommandBuilder.getDefinition(), - putCommandBuilder.getDefinition(), - patchCommandBuilder.getDefinition(), - deleteCommandBuilder.getDefinition(), - errorCommandBuilder.getDefinition(), + pingCommandBuilder.getDefinition(), + postCommandBuilder.getDefinition(), + putCommandBuilder.getDefinition(), + patchCommandBuilder.getDefinition(), + deleteCommandBuilder.getDefinition(), + errorCommandBuilder.getDefinition(), ] const subscriptionDefinitions: SubscriptionDefinitionList = [] export const theServiceV1Service = theServiceServiceBuilder - .addCommandDefinition(...commandDefinitions) - .addSubscriptionDefinition(...subscriptionDefinitions) + .addCommandDefinition(...commandDefinitions) + .addSubscriptionDefinition(...subscriptionDefinitions) diff --git a/packages/httpserver/tsconfig.json b/packages/httpserver/tsconfig.json index ac015033d..f5d860b04 100644 --- a/packages/httpserver/tsconfig.json +++ b/packages/httpserver/tsconfig.json @@ -1,21 +1,13 @@ { - "extends": "../../tsconfig.json", - "compilerOptions": { - "outDir": "./dist", - "declaration": true, - "sourceMap": false, - "declarationMap": true, - "types": [ - "vitest/globals", - "node" - ] - }, - "exclude": [ - "./**/*.d.ts" - ], - - "files": [ - "src/index.ts", - "fastify.d.ts" - ], -} \ No newline at end of file + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "./dist", + "declaration": true, + "sourceMap": false, + "declarationMap": true, + "types": ["vitest/globals", "node"] + }, + "exclude": ["./**/*.d.ts"], + "include": ["./src/**/*", "./test/*"], + "files": ["src/index.ts", "fastify.d.ts"] +} diff --git a/packages/httpserver/typedoc.json b/packages/httpserver/typedoc.json index 355bf0f98..71c4b2283 100644 --- a/packages/httpserver/typedoc.json +++ b/packages/httpserver/typedoc.json @@ -1,6 +1,5 @@ { - - "extends": ["../../typedoc.base.json"], - "entryPoints": ["src/index.ts"], - "tsconfig": "./tsconfig.json" -} \ No newline at end of file + "extends": ["../../typedoc.base.json"], + "entryPoints": ["src/index.ts"], + "tsconfig": "./tsconfig.json" +} diff --git a/packages/infisical-secret-store/docker-compose.yml b/packages/infisical-secret-store/docker-compose.yml index 5135eb27a..96dc7e543 100644 --- a/packages/infisical-secret-store/docker-compose.yml +++ b/packages/infisical-secret-store/docker-compose.yml @@ -1,5 +1,3 @@ -version: "3" - services: nginx: container_name: infisical-nginx diff --git a/packages/infisical-secret-store/jsr.json b/packages/infisical-secret-store/jsr.json new file mode 100644 index 000000000..8c636e343 --- /dev/null +++ b/packages/infisical-secret-store/jsr.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://jsr.io/schema/config-file.v1.json", + "name": "@purista/infisical-secret-store", + "version": "1.11.0", + "description": "State store with Infisical as database", + "keywords": ["purista", "secrets", "typescript", "javascript"], + "exports": "./dist/esm/index.js", + "publish": { + "include": ["dist/**/*.js", "dist/**/*.d.ts", "README.md", "package.json"], + "exclude": [ + "src", + ".github", + ".vscode", + ".zed", + "!dist", + "!dist/**/*.js", + "!dist/**/*.d.ts", + ".tshy", + ".tshy-build", + "vendor", + "docs", + "typedoc.json", + "..eslintcache", + ".npmignore" + ] + } +} diff --git a/packages/infisical-secret-store/package.json b/packages/infisical-secret-store/package.json index 53c6efa9e..bd06f0703 100644 --- a/packages/infisical-secret-store/package.json +++ b/packages/infisical-secret-store/package.json @@ -1,64 +1,61 @@ { - "name": "@purista/infisical-secret-store", - "version": "1.11.0", - "description": "State store with Infisical as database", - "homepage": "https://purista.dev", - "repository": { - "type": "git", - "url": "git@github.com:sebastianwessel/purista.git" - }, - "author": "Sebastian Wessel", - "license": "ISC", - "type": "module", - "main": "./dist/commonjs/index.js", - "exports": { - "./package.json": "./package.json", - ".": { - "import": { - "types": "./dist/esm/index.d.ts", - "default": "./dist/esm/index.js" - }, - "require": { - "types": "./dist/commonjs/index.d.ts", - "default": "./dist/commonjs/index.js" - } - } - }, - "files": [ - "dist/**/*" - ], - "publishConfig": { - "access": "public" - }, - "engines": { - "node": ">=18.15" - }, - "scripts": { - "lint": "eslint . --ext .ts,.json --cache . --fix", - "test": "vitest", - "build": "rimraf dist && tshy", - "env:up": "docker compose -f docker-compose.yml up -d", - "env:down": "docker compose -f docker-compose.yml down" - }, - "tshy": { - "exclude": [ - "src/**/*.test.ts" - ], - "exports": { - "./package.json": "./package.json", - ".": "./src/index.ts" - } - }, - "devDependencies": { - "@types/node": "^20.11.17", - "@types/sinon": "^17.0.3", - "sinon": "^17.0.1", - "tshy": "^1.11.1", - "vitest": "^1.3.0" - }, - "dependencies": { - "@purista/core": "*" - }, - "peerDependenciesMeta": {}, - "types": "./dist/commonjs/index.d.ts" + "name": "@purista/infisical-secret-store", + "version": "1.11.0", + "description": "State store with Infisical as database", + "homepage": "https://purista.dev", + "repository": { + "type": "git", + "url": "git@github.com:puristajs/purista.git" + }, + "author": "Sebastian Wessel", + "license": "ISC", + "type": "module", + "main": "./dist/commonjs/index.js", + "exports": { + "./package.json": "./package.json", + ".": { + "import": { + "types": "./dist/esm/index.d.ts", + "default": "./dist/esm/index.js" + }, + "require": { + "types": "./dist/commonjs/index.d.ts", + "default": "./dist/commonjs/index.js" + } + } + }, + "files": ["dist/**/*"], + "publishConfig": { + "access": "public" + }, + "engines": { + "node": ">=18.15" + }, + "scripts": { + "lint": "npx @biomejs/biome check --write", + "test": "vitest", + "build": "rimraf dist && tshy", + "env:up": "docker compose -f docker-compose.yml up -d", + "env:down": "docker compose -f docker-compose.yml down" + }, + "tshy": { + "exclude": ["src/**/*.test.ts"], + "exports": { + "./package.json": "./package.json", + ".": "./src/index.ts" + } + }, + "devDependencies": { + "@types/node": "^22.5.1", + "@types/sinon": "^17.0.3", + "sinon": "^19.0.2", + "tshy": "^3.0.2", + "vitest": "^3.0.4" + }, + "dependencies": { + "@purista/core": "*" + }, + "peerDependenciesMeta": {}, + "types": "./dist/commonjs/index.d.ts", + "module": "./dist/esm/index.js" } diff --git a/packages/infisical-secret-store/src/InfisicalClient/InfisicalClient.impl.ts b/packages/infisical-secret-store/src/InfisicalClient/InfisicalClient.impl.ts index 1da717855..c5ef7ed1b 100644 --- a/packages/infisical-secret-store/src/InfisicalClient/InfisicalClient.impl.ts +++ b/packages/infisical-secret-store/src/InfisicalClient/InfisicalClient.impl.ts @@ -9,181 +9,181 @@ import type { ClientConfig, HttpClientConfigCustom, Secret, TokenData } from './ * The internal http client to connect to the Infisical server. */ export class InfisicalClient extends HttpClient { - private serviceTokenSecret: string - private tokenData: TokenData | undefined - private projectKey: string = '' - - constructor(conf: ClientConfig) { - const config = { - name: 'InfisicalClient', - defaultHeaders: { - 'User-Agent': `InfisicalNodeSDK`, - 'content-type': 'application/json', - ...conf.defaultHeaders, - }, - ...conf, - } - super(config) - this.serviceTokenSecret = config.bearerToken.substring(config.bearerToken.lastIndexOf('.') + 1) - } - - /** - * Encrypt the given key, value and optional comment - */ - private encryptSecret(secretKey: string, secretValue: string, secretComment = '') { - const { - ciphertext: secretKeyCiphertext, - iv: secretKeyIV, - tag: secretKeyTag, - } = encrypt({ - text: secretKey, - secret: this.projectKey, - }) - - const { - ciphertext: secretValueCiphertext, - iv: secretValueIV, - tag: secretValueTag, - } = encrypt({ - text: secretValue, - secret: this.projectKey, - }) - - const { - ciphertext: secretCommentCiphertext, - iv: secretCommentIV, - tag: secretCommentTag, - } = encrypt({ - text: secretComment, - secret: this.projectKey, - }) - - return { - secretKeyCiphertext, - secretKeyIV, - secretKeyTag, - secretValueCiphertext, - secretValueIV, - secretValueTag, - secretCommentCiphertext, - secretCommentIV, - secretCommentTag, - } - } - - /** - * Fetches the token data from the server for given access token - */ - async getServiceTokenData() { - this.tokenData = await this.get('/api/v2/service-token') - - this.projectKey = decrypt({ - ciphertext: this.tokenData.encryptedKey, - iv: this.tokenData.iv, - tag: this.tokenData.tag, - secret: this.serviceTokenSecret, - }) - - return this.tokenData - } - - /** - * Get a single secret - */ - async getSecret(name: string) { - if (!this.tokenData) { - this.tokenData = await this.getServiceTokenData() - } - - const environment = this.tokenData.scopes[0]?.environment - - if (!environment) { - throw new UnhandledError(StatusCode.InvalidToken, 'Invalid service token - environment is missing') - } - - try { - const { secret: encryptedSecret } = await this.get<{ secret: Secret }>(encodeURI(`/api/v3/secrets/${name}`), { - query: { - environment, - workspaceId: this.tokenData.workspace, - type: SECRET_TYPE, - }, - }) - - return decrypt({ - ciphertext: encryptedSecret.secretValueCiphertext, - iv: encryptedSecret.secretValueIV, - tag: encryptedSecret.secretValueTag, - secret: this.projectKey, - }) - } catch (error) { - if (error instanceof UnhandledError) { - if (error.errorCode === StatusCode.NotFound) { - return undefined - } - } - throw error - } - } - - /** - * Set a secret. - * It will first try to update and if the secret does not exist, it will create a new one - */ - async setSecret(name: string, value: string) { - if (!this.tokenData) { - this.tokenData = await this.getServiceTokenData() - } - - const environment = this.tokenData.scopes[0]?.environment - - if (!environment) { - throw new UnhandledError(StatusCode.InvalidToken, 'Invalid service token - environment is missing') - } - - const payload = { - environment, - workspaceId: this.tokenData.workspace, - type: SECRET_TYPE, - ...this.encryptSecret(name, value), - } - - try { - await this.patch<{ secret: Secret }>(encodeURI(`/api/v3/secrets/${name}`), payload) - } catch (patchError) { - this.logger.debug({ err: patchError }, 'Secret seems to be a new one') - try { - await this.post<{ secret: Secret }>(encodeURI(`/api/v3/secrets/${name}`), payload) - } catch (error) { - const err = UnhandledError.fromError(error) - this.logger.error({ err }) - throw err - } - } - } - - /** - * Remove a secret - */ - async removeSecret(name: string) { - if (!this.tokenData) { - this.tokenData = await this.getServiceTokenData() - } - - const environment = this.tokenData.scopes[0]?.environment - - if (!environment) { - throw new UnhandledError(StatusCode.InvalidToken, 'Invalid service token - environment is missing') - } - - await this.delete( - encodeURI(`/api/v3/secrets/${name}`), - {}, - { - environment, - workspaceId: this.tokenData.workspace, - type: SECRET_TYPE, - }, - ) - } + private serviceTokenSecret: string + private tokenData: TokenData | undefined + private projectKey = '' + + constructor(conf: ClientConfig) { + const config = { + name: 'InfisicalClient', + defaultHeaders: { + 'User-Agent': 'InfisicalNodeSDK', + 'content-type': 'application/json', + ...conf.defaultHeaders, + }, + ...conf, + } + super(config) + this.serviceTokenSecret = config.bearerToken.substring(config.bearerToken.lastIndexOf('.') + 1) + } + + /** + * Encrypt the given key, value and optional comment + */ + private encryptSecret(secretKey: string, secretValue: string, secretComment = '') { + const { + ciphertext: secretKeyCiphertext, + iv: secretKeyIV, + tag: secretKeyTag, + } = encrypt({ + text: secretKey, + secret: this.projectKey, + }) + + const { + ciphertext: secretValueCiphertext, + iv: secretValueIV, + tag: secretValueTag, + } = encrypt({ + text: secretValue, + secret: this.projectKey, + }) + + const { + ciphertext: secretCommentCiphertext, + iv: secretCommentIV, + tag: secretCommentTag, + } = encrypt({ + text: secretComment, + secret: this.projectKey, + }) + + return { + secretKeyCiphertext, + secretKeyIV, + secretKeyTag, + secretValueCiphertext, + secretValueIV, + secretValueTag, + secretCommentCiphertext, + secretCommentIV, + secretCommentTag, + } + } + + /** + * Fetches the token data from the server for given access token + */ + async getServiceTokenData() { + this.tokenData = await this.get('/api/v2/service-token') + + this.projectKey = decrypt({ + ciphertext: this.tokenData.encryptedKey, + iv: this.tokenData.iv, + tag: this.tokenData.tag, + secret: this.serviceTokenSecret, + }) + + return this.tokenData + } + + /** + * Get a single secret + */ + async getSecret(name: string) { + if (!this.tokenData) { + this.tokenData = await this.getServiceTokenData() + } + + const environment = this.tokenData.scopes[0]?.environment + + if (!environment) { + throw new UnhandledError(StatusCode.InvalidToken, 'Invalid service token - environment is missing') + } + + try { + const { secret: encryptedSecret } = await this.get<{ secret: Secret }>(encodeURI(`/api/v3/secrets/${name}`), { + query: { + environment, + workspaceId: this.tokenData.workspace, + type: SECRET_TYPE, + }, + }) + + return decrypt({ + ciphertext: encryptedSecret.secretValueCiphertext, + iv: encryptedSecret.secretValueIV, + tag: encryptedSecret.secretValueTag, + secret: this.projectKey, + }) + } catch (error) { + if (error instanceof UnhandledError) { + if (error.errorCode === StatusCode.NotFound) { + return undefined + } + } + throw error + } + } + + /** + * Set a secret. + * It will first try to update and if the secret does not exist, it will create a new one + */ + async setSecret(name: string, value: string) { + if (!this.tokenData) { + this.tokenData = await this.getServiceTokenData() + } + + const environment = this.tokenData.scopes[0]?.environment + + if (!environment) { + throw new UnhandledError(StatusCode.InvalidToken, 'Invalid service token - environment is missing') + } + + const payload = { + environment, + workspaceId: this.tokenData.workspace, + type: SECRET_TYPE, + ...this.encryptSecret(name, value), + } + + try { + await this.patch<{ secret: Secret }>(encodeURI(`/api/v3/secrets/${name}`), payload) + } catch (patchError) { + this.logger.debug({ err: patchError }, 'Secret seems to be a new one') + try { + await this.post<{ secret: Secret }>(encodeURI(`/api/v3/secrets/${name}`), payload) + } catch (error) { + const err = UnhandledError.fromError(error) + this.logger.error({ err }) + throw err + } + } + } + + /** + * Remove a secret + */ + async removeSecret(name: string) { + if (!this.tokenData) { + this.tokenData = await this.getServiceTokenData() + } + + const environment = this.tokenData.scopes[0]?.environment + + if (!environment) { + throw new UnhandledError(StatusCode.InvalidToken, 'Invalid service token - environment is missing') + } + + await this.delete( + encodeURI(`/api/v3/secrets/${name}`), + {}, + { + environment, + workspaceId: this.tokenData.workspace, + type: SECRET_TYPE, + }, + ) + } } diff --git a/packages/infisical-secret-store/src/InfisicalClient/decrypt.impl.ts b/packages/infisical-secret-store/src/InfisicalClient/decrypt.impl.ts index a58462147..c27d73a46 100644 --- a/packages/infisical-secret-store/src/InfisicalClient/decrypt.impl.ts +++ b/packages/infisical-secret-store/src/InfisicalClient/decrypt.impl.ts @@ -4,12 +4,12 @@ import { ALGORITHM } from './constants.js' import type { DecryptInput } from './types/index.js' export const decrypt = (input: DecryptInput) => { - const { ciphertext, iv, tag, secret } = input - const decipher = createDecipheriv(ALGORITHM, secret, Buffer.from(iv, 'base64')) - decipher.setAuthTag(Buffer.from(tag, 'base64')) + const { ciphertext, iv, tag, secret } = input + const decipher = createDecipheriv(ALGORITHM, secret, Buffer.from(iv, 'base64')) + decipher.setAuthTag(Buffer.from(tag, 'base64')) - let cleartext = decipher.update(ciphertext, 'base64', 'utf8') - cleartext += decipher.final('utf8') + let cleartext = decipher.update(ciphertext, 'base64', 'utf8') + cleartext += decipher.final('utf8') - return cleartext + return cleartext } diff --git a/packages/infisical-secret-store/src/InfisicalClient/encrypt.impl.ts b/packages/infisical-secret-store/src/InfisicalClient/encrypt.impl.ts index 790bde6fe..cf1d06244 100644 --- a/packages/infisical-secret-store/src/InfisicalClient/encrypt.impl.ts +++ b/packages/infisical-secret-store/src/InfisicalClient/encrypt.impl.ts @@ -4,15 +4,15 @@ import { ALGORITHM, BLOCK_SIZE_BYTES } from './constants.js' import type { EncryptInput } from './types/index.js' export const encrypt = (input: EncryptInput) => { - const { text, secret } = input - const iv = randomBytes(BLOCK_SIZE_BYTES) - const cipher = createCipheriv(ALGORITHM, secret, iv) + const { text, secret } = input + const iv = randomBytes(BLOCK_SIZE_BYTES) + const cipher = createCipheriv(ALGORITHM, secret, iv) - let ciphertext = cipher.update(text, 'utf8', 'base64') - ciphertext += cipher.final('base64') - return { - ciphertext, - iv: iv.toString('base64'), - tag: cipher.getAuthTag().toString('base64'), - } + let ciphertext = cipher.update(text, 'utf8', 'base64') + ciphertext += cipher.final('base64') + return { + ciphertext, + iv: iv.toString('base64'), + tag: cipher.getAuthTag().toString('base64'), + } } diff --git a/packages/infisical-secret-store/src/InfisicalClient/types/ClientConfig.ts b/packages/infisical-secret-store/src/InfisicalClient/types/ClientConfig.ts index daf9dfa7d..b797f0bea 100644 --- a/packages/infisical-secret-store/src/InfisicalClient/types/ClientConfig.ts +++ b/packages/infisical-secret-store/src/InfisicalClient/types/ClientConfig.ts @@ -1,8 +1,8 @@ -import type { HttpClientConfig, Prettify } from '@purista/core' +import type { EmptyObject, HttpClientConfig, Prettify } from '@purista/core' -export type HttpClientConfigCustom = {} +export type HttpClientConfigCustom = EmptyObject export type ClientConfig = Prettify< - Required, 'bearerToken'>> & - Omit, 'bearerToken'> + Required, 'bearerToken'>> & + Omit, 'bearerToken'> > diff --git a/packages/infisical-secret-store/src/InfisicalClient/types/DecryptInput.ts b/packages/infisical-secret-store/src/InfisicalClient/types/DecryptInput.ts index 478bd612b..424ba653d 100644 --- a/packages/infisical-secret-store/src/InfisicalClient/types/DecryptInput.ts +++ b/packages/infisical-secret-store/src/InfisicalClient/types/DecryptInput.ts @@ -1,6 +1,6 @@ export type DecryptInput = { - ciphertext: string - iv: string - tag: string - secret: string + ciphertext: string + iv: string + tag: string + secret: string } diff --git a/packages/infisical-secret-store/src/InfisicalClient/types/Scope.ts b/packages/infisical-secret-store/src/InfisicalClient/types/Scope.ts index 2819b419c..cb847b649 100644 --- a/packages/infisical-secret-store/src/InfisicalClient/types/Scope.ts +++ b/packages/infisical-secret-store/src/InfisicalClient/types/Scope.ts @@ -1,5 +1,5 @@ export type Scope = { - environment: string - secretPath: string - _id: string + environment: string + secretPath: string + _id: string } diff --git a/packages/infisical-secret-store/src/InfisicalClient/types/Secret.ts b/packages/infisical-secret-store/src/InfisicalClient/types/Secret.ts index 9be753de1..e12c229db 100644 --- a/packages/infisical-secret-store/src/InfisicalClient/types/Secret.ts +++ b/packages/infisical-secret-store/src/InfisicalClient/types/Secret.ts @@ -1,19 +1,19 @@ export type Secret = { - _id: string - version: number - workspace: string - user?: string - type: 'shared' | 'personal' - environment: string - secretKeyCiphertext: string - secretKeyIV: string - secretKeyTag: string - secretValueCiphertext: string - secretValueIV: string - secretValueTag: string - secretCommentCiphertext?: string - secretCommentIV?: string - secretCommentTag?: string - createdAt: string - updatedAt: string + _id: string + version: number + workspace: string + user?: string + type: 'shared' | 'personal' + environment: string + secretKeyCiphertext: string + secretKeyIV: string + secretKeyTag: string + secretValueCiphertext: string + secretValueIV: string + secretValueTag: string + secretCommentCiphertext?: string + secretCommentIV?: string + secretCommentTag?: string + createdAt: string + updatedAt: string } diff --git a/packages/infisical-secret-store/src/InfisicalClient/types/TokenData.ts b/packages/infisical-secret-store/src/InfisicalClient/types/TokenData.ts index 9583452db..af8bd9bb1 100644 --- a/packages/infisical-secret-store/src/InfisicalClient/types/TokenData.ts +++ b/packages/infisical-secret-store/src/InfisicalClient/types/TokenData.ts @@ -1,24 +1,24 @@ import type { Scope } from './Scope.js' export type TokenData = { - _id: string - name: string - workspace: string - scopes: Scope[] - user: { - _id: string - authMethods: string[] - email: string - firstName: string - lastName: string - } - serviceAccount: string - lastUsed: Date - expiresAt: Date - encryptedKey: string - iv: string - tag: string - createdAt: string - updatedAt: string - permissions: string[] + _id: string + name: string + workspace: string + scopes: Scope[] + user: { + _id: string + authMethods: string[] + email: string + firstName: string + lastName: string + } + serviceAccount: string + lastUsed: Date + expiresAt: Date + encryptedKey: string + iv: string + tag: string + createdAt: string + updatedAt: string + permissions: string[] } diff --git a/packages/infisical-secret-store/src/InfisicalSecretStore.impl.ts b/packages/infisical-secret-store/src/InfisicalSecretStore.impl.ts index 35b381398..5a441e33e 100644 --- a/packages/infisical-secret-store/src/InfisicalSecretStore.impl.ts +++ b/packages/infisical-secret-store/src/InfisicalSecretStore.impl.ts @@ -37,50 +37,50 @@ console.log(value) // outputs: undefined ``` */ export class InfisicalSecretStore extends SecretStoreBaseClass { - public client: InfisicalClient - - constructor(config: StoreBaseConfig) { - super('InfisicalSecretStore', { enableCache: true, ...config }) - - this.client = new InfisicalClient({ - name: 'InfisicalClient', - ...config, - }) - } - - protected async getSecretImpl( - ...secretNames: SecretNames - ): Promise> { - const result: Record = {} - for (const name of secretNames) { - try { - result[name] = await this.client.getSecret(name) - } catch (err) { - const msg = `error in secret store getting value ${name}` - this.logger.error({ err }, msg) - throw new UnhandledError(StatusCode.InternalServerError, msg) - } - } - return result as ObjectWithKeysFromStringArray - } - - protected async removeSecretImpl(secretName: string) { - try { - await this.client.removeSecret(secretName) - } catch (err) { - const msg = `error in secret store removing value ${secretName}` - this.logger.error({ err }, msg) - throw new UnhandledError(StatusCode.InternalServerError, msg) - } - } - - protected async setSecretImpl(secretName: string, secretValue: string) { - try { - await this.client.setSecret(secretName, secretValue) - } catch (err) { - const msg = `error in secret store setting value ${secretName}` - this.logger.error({ err }, msg) - throw new UnhandledError(StatusCode.InternalServerError, msg) - } - } + public client: InfisicalClient + + constructor(config: StoreBaseConfig) { + super('InfisicalSecretStore', { enableCache: true, ...config }) + + this.client = new InfisicalClient({ + name: 'InfisicalClient', + ...config, + }) + } + + protected async getSecretImpl( + ...secretNames: SecretNames + ): Promise> { + const result: Record = {} + for (const name of secretNames) { + try { + result[name] = await this.client.getSecret(name) + } catch (err) { + const msg = `error in secret store getting value ${name}` + this.logger.error({ err }, msg) + throw new UnhandledError(StatusCode.InternalServerError, msg) + } + } + return result as ObjectWithKeysFromStringArray + } + + protected async removeSecretImpl(secretName: string) { + try { + await this.client.removeSecret(secretName) + } catch (err) { + const msg = `error in secret store removing value ${secretName}` + this.logger.error({ err }, msg) + throw new UnhandledError(StatusCode.InternalServerError, msg) + } + } + + protected async setSecretImpl(secretName: string, secretValue: string) { + try { + await this.client.setSecret(secretName, secretValue) + } catch (err) { + const msg = `error in secret store setting value ${secretName}` + this.logger.error({ err }, msg) + throw new UnhandledError(StatusCode.InternalServerError, msg) + } + } } diff --git a/packages/infisical-secret-store/src/index.test.ts b/packages/infisical-secret-store/src/index.test.ts index 6d2d2fe11..2a1f7a47a 100644 --- a/packages/infisical-secret-store/src/index.test.ts +++ b/packages/infisical-secret-store/src/index.test.ts @@ -1,11 +1,11 @@ import { InfisicalSecretStore, puristaVersion } from './index.js' describe('exports InfisicalSecretStore', () => { - it('has a version', () => { - expect(puristaVersion).toBeDefined() - }) + it('has a version', () => { + expect(puristaVersion).toBeDefined() + }) - it('exports InfisicalSecretStore', () => { - expect(InfisicalSecretStore).toBeDefined() - }) + it('exports InfisicalSecretStore', () => { + expect(InfisicalSecretStore).toBeDefined() + }) }) diff --git a/packages/infisical-secret-store/test/integration.test.ts b/packages/infisical-secret-store/test/integration.test.ts index 669b8ec24..d01d85849 100644 --- a/packages/infisical-secret-store/test/integration.test.ts +++ b/packages/infisical-secret-store/test/integration.test.ts @@ -13,47 +13,47 @@ st.64746e08fd775a75b3f6e6db.82cd9d993fa4e5981a8132bdadea6adc.0c902c3ca70cb49d996 */ describe.skip('Infisical secret store', () => { - const baseUrl = 'http://localhost:8080/' - - beforeAll(async () => { - execSync(`cd ${resolve(__dirname, '../')} && npm run env:up`) - - await new Promise((resolve) => { - setTimeout(() => { - resolve(undefined) - }, 5000) - }) - }) - - afterAll(async () => { - execSync(`cd ${resolve(__dirname, '../')} && npm run env:down`) - }) - - const store = new InfisicalSecretStore({ - bearerToken: 'st.64f099f22d1513b001b732b6.61aa81633200b752868d9ca701ce1a61.0dd80b7bf98043da184dc719233d476d', - baseUrl, - enableGet: true, - enableRemove: true, - enableSet: true, - logger: getLoggerMock().mock, - }) - - it('set a secret key', async () => { - await expect(store.setSecret('test', 'my-value')).resolves.toBeUndefined() - }) - - it('gets a secret key', async () => { - await expect(store.getSecret('test')).resolves.toStrictEqual({ test: 'my-value' }) - }) - - it('updates a secret key', async () => { - await expect(store.setSecret('test', 'my-value-updated')).resolves.toBeUndefined() - await expect(store.getSecret('test')).resolves.toStrictEqual({ test: 'my-value-updated' }) - }) - - it('removes a secret key', async () => { - await expect(store.getSecret('test')).resolves.toStrictEqual({ test: 'my-value-updated' }) - await expect(store.removeSecret('test')).resolves.toBeUndefined() - await expect(store.getSecret('test')).resolves.toStrictEqual({ test: undefined }) - }) + const baseUrl = 'http://localhost:8080/' + + beforeAll(async () => { + execSync(`cd ${resolve(__dirname, '../')} && npm run env:up`) + + await new Promise(resolve => { + setTimeout(() => { + resolve(undefined) + }, 5000) + }) + }) + + afterAll(async () => { + execSync(`cd ${resolve(__dirname, '../')} && npm run env:down`) + }) + + const store = new InfisicalSecretStore({ + bearerToken: 'st.64f099f22d1513b001b732b6.61aa81633200b752868d9ca701ce1a61.0dd80b7bf98043da184dc719233d476d', + baseUrl, + enableGet: true, + enableRemove: true, + enableSet: true, + logger: getLoggerMock().mock, + }) + + it('set a secret key', async () => { + await expect(store.setSecret('test', 'my-value')).resolves.toBeUndefined() + }) + + it('gets a secret key', async () => { + await expect(store.getSecret('test')).resolves.toStrictEqual({ test: 'my-value' }) + }) + + it('updates a secret key', async () => { + await expect(store.setSecret('test', 'my-value-updated')).resolves.toBeUndefined() + await expect(store.getSecret('test')).resolves.toStrictEqual({ test: 'my-value-updated' }) + }) + + it('removes a secret key', async () => { + await expect(store.getSecret('test')).resolves.toStrictEqual({ test: 'my-value-updated' }) + await expect(store.removeSecret('test')).resolves.toBeUndefined() + await expect(store.getSecret('test')).resolves.toStrictEqual({ test: undefined }) + }) }) diff --git a/packages/infisical-secret-store/tsconfig.json b/packages/infisical-secret-store/tsconfig.json index 42dac6999..a2f689e2f 100644 --- a/packages/infisical-secret-store/tsconfig.json +++ b/packages/infisical-secret-store/tsconfig.json @@ -1,21 +1,13 @@ { - "extends": "../../tsconfig.json", - "compilerOptions": { - "outDir": "./dist", - "declaration": true, - "sourceMap": false, - "declarationMap": true, - "types": [ - "vitest/globals", - "node" - ] - }, - "exclude": [ - "./**/*.d.ts" - ], - - "include": [ - "./src/**/*", - "./test/*", - ], -} \ No newline at end of file + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "./dist", + "declaration": true, + "sourceMap": false, + "declarationMap": true, + "types": ["vitest/globals", "node"] + }, + "exclude": ["./**/*.d.ts"], + + "include": ["./src/**/*", "./test/*"] +} diff --git a/packages/infisical-secret-store/typedoc.json b/packages/infisical-secret-store/typedoc.json index 355bf0f98..71c4b2283 100644 --- a/packages/infisical-secret-store/typedoc.json +++ b/packages/infisical-secret-store/typedoc.json @@ -1,6 +1,5 @@ { - - "extends": ["../../typedoc.base.json"], - "entryPoints": ["src/index.ts"], - "tsconfig": "./tsconfig.json" -} \ No newline at end of file + "extends": ["../../typedoc.base.json"], + "entryPoints": ["src/index.ts"], + "tsconfig": "./tsconfig.json" +} diff --git a/packages/k8s-sdk/jsr.json b/packages/k8s-sdk/jsr.json new file mode 100644 index 000000000..3e0309a48 --- /dev/null +++ b/packages/k8s-sdk/jsr.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://jsr.io/schema/config-file.v1.json", + "name": "@purista/k8s-sdk", + "version": "1.11.0", + "description": "SDK and helper to run PURISTA services in Kubernetes", + "keywords": ["purista", "k8s", "typescript", "javascript"], + "exports": "./dist/esm/index.js", + "publish": { + "include": ["dist/**/*.js", "dist/**/*.d.ts", "README.md", "package.json"], + "exclude": [ + "src", + ".github", + ".vscode", + ".zed", + "!dist", + "!dist/**/*.js", + "!dist/**/*.d.ts", + ".tshy", + ".tshy-build", + "vendor", + "docs", + "typedoc.json", + "..eslintcache", + ".npmignore" + ] + } +} diff --git a/packages/k8s-sdk/package.json b/packages/k8s-sdk/package.json index fa5925d38..b2dc12071 100644 --- a/packages/k8s-sdk/package.json +++ b/packages/k8s-sdk/package.json @@ -1,73 +1,70 @@ { - "name": "@purista/k8s-sdk", - "version": "1.11.0", - "description": "SDK and helper to run PURISTA services in Kubernetes", - "homepage": "https://purista.dev", - "repository": { - "type": "git", - "url": "git@github.com:sebastianwessel/purista.git" - }, - "author": "Sebastian Wessel", - "license": "ISC", - "type": "module", - "main": "./dist/commonjs/index.js", - "exports": { - "./package.json": "./package.json", - ".": { - "import": { - "types": "./dist/esm/index.d.ts", - "default": "./dist/esm/index.js" - }, - "require": { - "types": "./dist/commonjs/index.d.ts", - "default": "./dist/commonjs/index.js" - } - } - }, - "files": [ - "dist/**/*" - ], - "publishConfig": { - "access": "public" - }, - "engines": { - "node": ">=18.15" - }, - "scripts": { - "lint": "eslint . --ext .ts,.json --cache . --fix", - "test": "vitest", - "build": "rimraf dist && tshy" - }, - "tshy": { - "exclude": [ - "src/**/*.test.ts" - ], - "exports": { - "./package.json": "./package.json", - ".": "./src/index.ts" - } - }, - "devDependencies": { - "@hono/node-server": "^1.8.0", - "@types/node": "^20.11.17", - "@types/sinon": "^17.0.3", - "sinon": "^17.0.1", - "tshy": "^1.11.1", - "vitest": "^1.3.0" - }, - "dependencies": { - "@opentelemetry/api": "^1.7.0", - "@opentelemetry/semantic-conventions": "^1.19.0", - "@purista/core": "*", - "hono": "^4.0.4" - }, - "peerDependencies": { - "@hono/node-server": "^1.4.0" - }, - "peerDependenciesMeta": { - "@hono/node-server": { - "optional": true - } - }, - "types": "./dist/commonjs/index.d.ts" + "name": "@purista/k8s-sdk", + "version": "1.11.0", + "description": "SDK and helper to run PURISTA services in Kubernetes", + "homepage": "https://purista.dev", + "repository": { + "type": "git", + "url": "git@github.com:puristajs/purista.git" + }, + "author": "Sebastian Wessel", + "license": "ISC", + "type": "module", + "main": "./dist/commonjs/index.js", + "exports": { + "./package.json": "./package.json", + ".": { + "import": { + "types": "./dist/esm/index.d.ts", + "default": "./dist/esm/index.js" + }, + "require": { + "types": "./dist/commonjs/index.d.ts", + "default": "./dist/commonjs/index.js" + } + } + }, + "files": ["dist/**/*"], + "publishConfig": { + "access": "public" + }, + "engines": { + "node": ">=18.15" + }, + "scripts": { + "lint": "npx @biomejs/biome check --write", + "test": "vitest", + "build": "rimraf dist && tshy" + }, + "tshy": { + "exclude": ["src/**/*.test.ts"], + "exports": { + "./package.json": "./package.json", + ".": "./src/index.ts" + } + }, + "devDependencies": { + "@hono/node-server": "^1.12.2", + "@types/node": "^22.5.1", + "@types/sinon": "^17.0.3", + "sinon": "^19.0.2", + "tshy": "^3.0.2", + "vitest": "^3.0.4" + }, + "dependencies": { + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/semantic-conventions": "^1.27.0", + "@purista/core": "*", + "hono": "^4.4.7" + }, + "peerDependencies": { + "@hono/node-server": "^1.12.0" + }, + "peerDependenciesMeta": { + "@hono/node-server": { + "optional": true + } + }, + "types": "./dist/commonjs/index.d.ts", + "module": "./dist/esm/index.js" } diff --git a/packages/k8s-sdk/src/addServiceEndpoints.impl.ts b/packages/k8s-sdk/src/addServiceEndpoints.impl.ts index 28def8d51..d98842f04 100644 --- a/packages/k8s-sdk/src/addServiceEndpoints.impl.ts +++ b/packages/k8s-sdk/src/addServiceEndpoints.impl.ts @@ -1,20 +1,23 @@ import { posix } from 'node:path' -import { context, propagation, SpanKind, SpanStatusCode } from '@opentelemetry/api' -import { SemanticAttributes } from '@opentelemetry/semantic-conventions' +import { SpanKind, SpanStatusCode, context, propagation } from '@opentelemetry/api' +import { ATTR_URL_FULL } from '@opentelemetry/semantic-conventions' + +import { ATTR_HTTP_HOST, ATTR_HTTP_METHOD, ATTR_HTTP_STATUS_CODE } from '@opentelemetry/semantic-conventions/incubating' + import type { Command, HttpExposedServiceMeta, Logger, Service } from '@purista/core' import { - EBMessageType, - HandledError, - isCommandErrorResponse, - isHttpExposedServiceMeta, - PuristaSpanName, - serializeOtp, - StatusCode, - UnhandledError, + EBMessageType, + HandledError, + PuristaSpanName, + StatusCode, + UnhandledError, + isCommandErrorResponse, + isHttpExposedServiceMeta, + serializeOtp, } from '@purista/core' -import type { Context as HonoContext, Hono } from 'hono' -import type { StatusCode as HonoStatusCode } from 'hono/utils/http-status' +import type { Hono, Context as HonoContext } from 'hono' +import type { ContentfulStatusCode } from 'hono/utils/http-status' /** * @@ -25,185 +28,188 @@ import type { StatusCode as HonoStatusCode } from 'hono/utils/http-status' * @returns */ export const addServiceEndpoints = ( - services: Service | Service[] | undefined, - app: Hono, - logger: Logger, - apiMountPath = '/api', + services: Service | Service[] | undefined, + app: Hono, + logger: Logger, + apiMountPath = '/api', ) => { - if (!services) { - return - } - - const exposedServices = Array.isArray(services) ? services : [services] - exposedServices.forEach((service) => { - service.commandDefinitionList.forEach((definition) => { - const metadata = definition.metadata as HttpExposedServiceMeta - if (!isHttpExposedServiceMeta(metadata)) { - logger.debug('...skip exposing function') - return - } - - const data = metadata.expose - const serviceVersion = service.info.serviceVersion - const method = metadata.expose.http.method - const url = posix.join(apiMountPath || '/api', `v${serviceVersion}`, data.http.path) - - const handler = async (c: HonoContext) => { - const parentContext = propagation.extract(context.active(), c.req.raw.headers) - - return await service - .getTracer('PURISTA_k8s_http_server') - .startActiveSpan( - PuristaSpanName.KubernetesHttpRequest, - { kind: SpanKind.SERVER }, - parentContext, - async (span) => { - const hostname = process.env.HOSTNAME ?? 'unknown' - - span.setAttribute(SemanticAttributes.HTTP_URL, c.req.url || '') - span.setAttribute(SemanticAttributes.HTTP_METHOD, c.req.method || '') - span.setAttribute(SemanticAttributes.HTTP_HOST, hostname) - - try { - const queryParams: Record = {} - - // allow only defined parameters - if (metadata.expose.http.openApi?.query) { - const parsedQueries = c.req.query() - metadata.expose.http.openApi.query.forEach((qp) => { - queryParams[qp.name] = parsedQueries[qp.name] - if (qp.required && !parsedQueries[qp.name]) { - throw new HandledError(StatusCode.BadRequest, `query parameter ${qp.name} is required`) - } - }) - } - - let body - if (c.req.method === 'POST' || c.req.method === 'PUT' || c.req.method === 'PATCH') { - body = await c.req.json() - } - - const command: Command = { - id: '', - messageType: EBMessageType.Command, - correlationId: '', - timestamp: Date.now(), - contentType: definition.metadata.expose.contentTypeResponse ?? 'application/json', - contentEncoding: definition.metadata.expose.contentEncodingResponse ?? 'utf-8', - otp: serializeOtp(), - receiver: { - serviceName: service.info.serviceName, - serviceVersion: service.info.serviceVersion, - serviceTarget: definition.commandName, - }, - sender: { - serviceName: '', - serviceVersion: '', - serviceTarget: '', - instanceId: '', - }, - payload: { - payload: body, - parameter: { - ...queryParams, - parameter: c.req.param(), - }, - }, - } - - const result = await service.executeCommand(command) - - if (isCommandErrorResponse(result)) { - span.setAttribute(SemanticAttributes.HTTP_STATUS_CODE, result.payload.status) - - span.setStatus({ - code: SpanStatusCode.ERROR, - message: result.payload.message, - }) - - const response = c.json(result.payload, result.payload.status as any) - span.end() - return response - } - - const header: Record = { - 'content-type': `${metadata.expose.contentTypeResponse ?? 'application/json'}; charset=${ - metadata.expose.contentEncodingResponse ?? 'utf-8' - }`, - } - - propagation.inject(context.active(), header) - - // empty response - if (result.payload === undefined || result.payload === '') { - span.setAttribute(SemanticAttributes.HTTP_STATUS_CODE, StatusCode.NoContent) - span.end() - - c.status(StatusCode.NoContent) - Object.values(header).forEach((val) => c.header(val[0], val[1])) - return c.body(null) - } - - span.setAttribute(SemanticAttributes.HTTP_STATUS_CODE, StatusCode.OK) - - const response = - result.contentType !== 'application/json' - ? c.text(result.payload as string, StatusCode.OK, header) - : c.json(result.payload, StatusCode.OK, header) - - span.end() - return response - } catch (error) { - const err = - error instanceof HandledError || error instanceof UnhandledError - ? error - : UnhandledError.fromError(error) - - logger.error({ err, ...span.spanContext() }, err.message) - - span.setStatus({ - code: SpanStatusCode.ERROR, - message: err.message, - }) - - span.recordException(err) - - const header: Record = { - 'content-type': `${metadata.expose.contentTypeResponse ?? 'application/json'}; charset=${ - metadata.expose.contentEncodingResponse ?? 'utf-8' - }`, - } - - propagation.inject(context.active(), header) - - const response = c.json(err.getErrorResponse(), err.errorCode as HonoStatusCode, header) - span.end() - return response - } - }, - ) - } - - logger.debug({ method, url }, `adding ${url}`) - switch (method) { - case 'GET': - app.get(url, handler) - break - case 'POST': - app.post(url, handler) - break - case 'PATCH': - app.patch(url, handler) - break - case 'PUT': - app.put(url, handler) - break - case 'DELETE': - app.delete(url, handler) - break - default: - break - } - }) - }) + if (!services) { + return + } + + const exposedServices = Array.isArray(services) ? services : [services] + for (const service of exposedServices) { + for (const definition of service.commandDefinitionList) { + const metadata = definition.metadata as HttpExposedServiceMeta + if (!isHttpExposedServiceMeta(metadata)) { + logger.debug('...skip exposing function') + return + } + + const data = metadata.expose + const serviceVersion = service.info.serviceVersion + const method = metadata.expose.http.method + const url = posix.join(apiMountPath || '/api', `v${serviceVersion}`, data.http.path) + + const handler = async (c: HonoContext) => { + const parentContext = propagation.extract(context.active(), c.req.raw.headers) + + return await service + .getTracer('PURISTA_k8s_http_server') + .startActiveSpan( + PuristaSpanName.KubernetesHttpRequest, + { kind: SpanKind.SERVER }, + parentContext, + async span => { + const hostname = process.env.HOSTNAME ?? 'unknown' + + span.setAttribute(ATTR_URL_FULL, c.req.url || '') + span.setAttribute(ATTR_HTTP_METHOD, c.req.method || '') + span.setAttribute(ATTR_HTTP_HOST, hostname) + + try { + const queryParams: Record = {} + + // allow only defined parameters + if (metadata.expose.http.openApi?.query) { + const parsedQueries = c.req.query() + for (const qp of metadata.expose.http.openApi.query) { + queryParams[qp.name] = parsedQueries[qp.name] + if (qp.required && !parsedQueries[qp.name]) { + throw new HandledError(StatusCode.BadRequest, `query parameter ${qp.name} is required`) + } + } + } + + let body: unknown + if (c.req.method === 'POST' || c.req.method === 'PUT' || c.req.method === 'PATCH') { + body = await c.req.json() + } + + const command: Command = { + id: '', + messageType: EBMessageType.Command, + correlationId: '', + timestamp: Date.now(), + contentType: definition.metadata.expose.contentTypeResponse ?? 'application/json', + contentEncoding: definition.metadata.expose.contentEncodingResponse ?? 'utf-8', + otp: serializeOtp(), + receiver: { + serviceName: service.info.serviceName, + serviceVersion: service.info.serviceVersion, + serviceTarget: definition.commandName, + }, + sender: { + serviceName: '', + serviceVersion: '', + serviceTarget: '', + instanceId: '', + }, + payload: { + payload: body, + parameter: { + ...queryParams, + parameter: c.req.param(), + }, + }, + } + + const result = await service.executeCommand(command) + + if (isCommandErrorResponse(result)) { + span.setAttribute(ATTR_HTTP_STATUS_CODE, result.payload.status) + + span.setStatus({ + code: SpanStatusCode.ERROR, + message: result.payload.message, + }) + + const response = c.json(result.payload, result.payload.status as ContentfulStatusCode) + span.end() + return response + } + + const header: Record = { + 'content-type': `${metadata.expose.contentTypeResponse ?? 'application/json'}; charset=${ + metadata.expose.contentEncodingResponse ?? 'utf-8' + }`, + } + + propagation.inject(context.active(), header) + + // empty response + if (result.payload === undefined || result.payload === '') { + span.setAttribute(ATTR_HTTP_STATUS_CODE, StatusCode.NoContent) + span.end() + + c.status(StatusCode.NoContent) + + for (const val of Object.entries(header)) { + c.header(val[0], val[1]) + } + return c.body(null) + } + + span.setAttribute(ATTR_HTTP_STATUS_CODE, StatusCode.OK) + + const response = + result.contentType !== 'application/json' + ? c.text(result.payload as string, StatusCode.OK, header) + : c.json(result.payload, StatusCode.OK, header) + + span.end() + return response + } catch (error) { + const err = + error instanceof HandledError || error instanceof UnhandledError + ? error + : UnhandledError.fromError(error) + + logger.error({ err, ...span.spanContext() }, err.message) + + span.setStatus({ + code: SpanStatusCode.ERROR, + message: err.message, + }) + + span.recordException(err) + + const header: Record = { + 'content-type': `${metadata.expose.contentTypeResponse ?? 'application/json'}; charset=${ + metadata.expose.contentEncodingResponse ?? 'utf-8' + }`, + } + + propagation.inject(context.active(), header) + + const response = c.json(err.getErrorResponse(), err.errorCode as ContentfulStatusCode, header) + span.end() + return response + } + }, + ) + } + + logger.debug({ method, url }, `adding ${url}`) + switch (method) { + case 'GET': + app.get(url, handler) + break + case 'POST': + app.post(url, handler) + break + case 'PATCH': + app.patch(url, handler) + break + case 'PUT': + app.put(url, handler) + break + case 'DELETE': + app.delete(url, handler) + break + default: + break + } + } + } } diff --git a/packages/k8s-sdk/src/getHttpServer.impl.ts b/packages/k8s-sdk/src/getHttpServer.impl.ts index d1f9adf26..8250875b1 100644 --- a/packages/k8s-sdk/src/getHttpServer.impl.ts +++ b/packages/k8s-sdk/src/getHttpServer.impl.ts @@ -1,7 +1,7 @@ import { StatusCode, UnhandledError } from '@purista/core' import { Hono } from 'hono' import { compress } from 'hono/compress' -import type { StatusCode as HonoStatusCode } from 'hono/utils/http-status' +import type { ContentfulStatusCode } from 'hono/utils/http-status' import { addServiceEndpoints } from './addServiceEndpoints.impl.js' import type { GetHttpServerConfig } from './types.js' @@ -19,65 +19,65 @@ import { puristaVersion } from './version.js' * @returns a object with server, router, start and destroy functions and name var */ export const getHttpServer = (input: GetHttpServerConfig, name = 'K8sHttpHelperServer') => { - const { healthFn, services, hostname, apiMountPath } = input - - const hostnameWithFallback = hostname ?? process.env.HOSTNAME - - const logger = input.logger.getChildLogger({ name, puristaVersion, hostname: hostnameWithFallback }) - const app = new Hono() - - if (input.enableHttpCompression ?? input.enableHttpCompression === undefined) { - app.use('*', compress()) - } - - app.onError((error, c) => { - const err = UnhandledError.fromError(error) - logger.error(`${err}`) - return c.json(err.getErrorResponse(), err.errorCode as HonoStatusCode) - }) - - app.notFound(async (c) => { - const err = new UnhandledError(StatusCode.NotFound, 'endpoint not found') - logger.error(`${err}`) - return c.json(err.getErrorResponse(), err.errorCode as HonoStatusCode) - }) - - let isShuttingDown = false - - process.once('SIGTERM', () => { - isShuttingDown = true - }) - - process.on('uncaughtException', (error, origin) => { - const err = UnhandledError.fromError(error) - logger.error({ err, origin }, `unhandled error: ${err.message}`) - }) - - process.on('unhandledRejection', (error, origin) => { - const err = UnhandledError.fromError(error) - logger.error({ err, origin }, `unhandled rejection: ${err.message}`) - }) - - if (!input.disableEndpointExposing) { - addServiceEndpoints(services, app, logger, apiMountPath) - } - - app.get('/healthz', async (c) => { - const isHealthy = await healthFn() - if (isShuttingDown) { - const err = new UnhandledError(StatusCode.ServiceUnavailable, 'shut down in progress') - return c.json(err.getErrorResponse(), err.errorCode as HonoStatusCode) - } - - if (isHealthy) { - const err = new UnhandledError(StatusCode.OK, 'ok') - return c.json(err.getErrorResponse(), err.errorCode as HonoStatusCode) - } - logger.error('health not ok') - - const err = new UnhandledError(StatusCode.InternalServerError, 'not ok') - return c.json(err.getErrorResponse(), err.errorCode as HonoStatusCode) - }) - - return app + const { healthFn, services, hostname, apiMountPath } = input + + const hostnameWithFallback = hostname ?? process.env.HOSTNAME + + const logger = input.logger.getChildLogger({ name, puristaVersion, hostname: hostnameWithFallback }) + const app = new Hono() + + if (input.enableHttpCompression ?? input.enableHttpCompression === undefined) { + app.use('*', compress()) + } + + app.onError((error, c) => { + const err = UnhandledError.fromError(error) + logger.error(`${err}`) + return c.json(err.getErrorResponse(), err.errorCode as ContentfulStatusCode) + }) + + app.notFound(async c => { + const err = new UnhandledError(StatusCode.NotFound, 'endpoint not found') + logger.error(`${err}`) + return c.json(err.getErrorResponse(), err.errorCode as ContentfulStatusCode) + }) + + let isShuttingDown = false + + process.once('SIGTERM', () => { + isShuttingDown = true + }) + + process.on('uncaughtException', (error, origin) => { + const err = UnhandledError.fromError(error) + logger.error({ err, origin }, `unhandled error: ${err.message}`) + }) + + process.on('unhandledRejection', (error, origin) => { + const err = UnhandledError.fromError(error) + logger.error({ err, origin }, `unhandled rejection: ${err.message}`) + }) + + if (!input.disableEndpointExposing) { + addServiceEndpoints(services, app, logger, apiMountPath) + } + + app.get('/healthz', async c => { + const isHealthy = await healthFn() + if (isShuttingDown) { + const err = new UnhandledError(StatusCode.ServiceUnavailable, 'shut down in progress') + return c.json(err.getErrorResponse(), err.errorCode as ContentfulStatusCode) + } + + if (isHealthy) { + const err = new UnhandledError(StatusCode.OK, 'ok') + return c.json(err.getErrorResponse(), err.errorCode as ContentfulStatusCode) + } + logger.error('health not ok') + + const err = new UnhandledError(StatusCode.InternalServerError, 'not ok') + return c.json(err.getErrorResponse(), err.errorCode as ContentfulStatusCode) + }) + + return app } diff --git a/packages/k8s-sdk/src/index.test.ts b/packages/k8s-sdk/src/index.test.ts index 1da00b560..7c20f3ada 100644 --- a/packages/k8s-sdk/src/index.test.ts +++ b/packages/k8s-sdk/src/index.test.ts @@ -1,11 +1,11 @@ import { getHttpServer, puristaVersion } from './index.js' describe('exports k8s-sdk', () => { - it('has a version', () => { - expect(puristaVersion).toBeDefined() - }) + it('has a version', () => { + expect(puristaVersion).toBeDefined() + }) - it('exports getHttpServer', () => { - expect(getHttpServer).toBeDefined() - }) + it('exports getHttpServer', () => { + expect(getHttpServer).toBeDefined() + }) }) diff --git a/packages/k8s-sdk/src/index.ts b/packages/k8s-sdk/src/index.ts index a42275d50..01419e761 100644 --- a/packages/k8s-sdk/src/index.ts +++ b/packages/k8s-sdk/src/index.ts @@ -112,14 +112,14 @@ main() * @module */ declare global { - interface FetchEvent extends Event { - readonly request: Request - respondWith(response: Promise | Response): Promise - } - interface ExecutionContext { - waitUntil(promise: Promise): void - passThroughOnException(): void - } + interface FetchEvent extends Event { + readonly request: Request + respondWith(response: Promise | Response): Promise + } + interface ExecutionContext { + waitUntil(promise: Promise): void + passThroughOnException(): void + } } export * from './addServiceEndpoints.impl.js' diff --git a/packages/k8s-sdk/src/types.ts b/packages/k8s-sdk/src/types.ts index cb2896e89..f4cfb2e52 100644 --- a/packages/k8s-sdk/src/types.ts +++ b/packages/k8s-sdk/src/types.ts @@ -4,18 +4,18 @@ import type { Logger, Service } from '@purista/core' * The configuration object for creating the k8s http server */ export type GetHttpServerConfig = { - /** a logger instance */ - logger: Logger - /** hostname used in tracing and logging */ - hostname?: string - /** health function to be executed on health check */ - healthFn: () => Promise - /** service or array of services which should expose their commands as endpoints if defined */ - services?: Service | Service[] - /** disables adding of all endpoints for commands which are marked to be exposed as http endpoints */ - disableEndpointExposing?: boolean - /** the api mount path @default /api */ - apiMountPath?: string - /** enable HTTP compression in web server @default true */ - enableHttpCompression?: boolean + /** a logger instance */ + logger: Logger + /** hostname used in tracing and logging */ + hostname?: string + /** health function to be executed on health check */ + healthFn: () => Promise + /** service or array of services which should expose their commands as endpoints if defined */ + services?: Service | Service[] + /** disables adding of all endpoints for commands which are marked to be exposed as http endpoints */ + disableEndpointExposing?: boolean + /** the api mount path @default /api */ + apiMountPath?: string + /** enable HTTP compression in web server @default true */ + enableHttpCompression?: boolean } diff --git a/packages/k8s-sdk/test/integration.test.ts b/packages/k8s-sdk/test/integration.test.ts index a7493ce2c..7d2885c74 100644 --- a/packages/k8s-sdk/test/integration.test.ts +++ b/packages/k8s-sdk/test/integration.test.ts @@ -1,144 +1,143 @@ import { serve } from '@hono/node-server' -import { DefaultEventBridge, getLoggerMock, HttpClient, StatusCode, UnhandledError } from '@purista/core' +import { DefaultEventBridge, HttpClient, StatusCode, UnhandledError, getLoggerMock } from '@purista/core' import { createSandbox } from 'sinon' import { getHttpServer } from '../src/index.js' import { theServiceV1Service } from './service/theService/v1/index.js' describe('getHttpServer', () => { - let logger: ReturnType - let server: ReturnType - let app: ReturnType - let eventBridge: DefaultEventBridge - const disableEndpointExposing = false - - const sandbox = createSandbox() - - const port = 8082 - - beforeAll(async () => { - logger = getLoggerMock(sandbox) - - eventBridge = new DefaultEventBridge({ logger: getLoggerMock().mock }) - - // set up the service - const theService = await theServiceV1Service.getInstance(eventBridge, { logger: getLoggerMock().mock }) - await theService.start() - - app = getHttpServer({ - healthFn: async () => true, - services: [theService], - hostname: 'localhost', - apiMountPath: '/api', - disableEndpointExposing, - logger: logger.mock, - }) - - server = serve({ - fetch: app.fetch, - port, - }) - }) - - afterEach(() => { - sandbox.reset() - sandbox.restore() - }) - - afterAll(async () => { - await eventBridge.destroy() - - const s = server as any - if (s.closeAllConnections) { - await s.closeAllConnections() - } - - await server.close() - }) - - it('returns healthz', async () => { - const client = new HttpClient({ baseUrl: `http://127.0.0.1:${port}` }) - - await expect(client.get('healthz')).resolves.toMatchObject({ - status: 200, - message: 'ok', - }) - }) - - afterEach(() => { - logger.stubs.info.resetHistory() - logger.stubs.error.resetHistory() - logger.stubs.warn.resetHistory() - logger.stubs.debug.resetHistory() - logger.stubs.fatal.resetHistory() - }) - - it('exposes http get endpoint', async () => { - const client = new HttpClient({ baseUrl: `http://127.0.0.1:${port}`, logger: getLoggerMock().mock }) - await expect(client.get('/api/v1/ping', { query: { required: 'true' } })).resolves.toMatchObject({ ping: true }) - }) - - it('returns a error on invalid query parameter', async () => { - const client = new HttpClient({ baseUrl: `http://127.0.0.1:${port}`, logger: getLoggerMock().mock }) - await expect(client.get('/api/v1/ping')).rejects.toStrictEqual( - new UnhandledError(StatusCode.BadRequest, 'Bad Request'), - ) - }) - - it('has a 404 handling', async () => { - const client = new HttpClient({ baseUrl: `http://127.0.0.1:${port}`, logger: getLoggerMock().mock }) - await expect(client.get('/api/v1/unknown')).rejects.toStrictEqual( - new UnhandledError(StatusCode.NotFound, 'Not Found'), - ) - }) - - it('returns a error if command returns error', async () => { - const client = new HttpClient({ baseUrl: `http://127.0.0.1:${port}`, logger: getLoggerMock().mock }) - await expect(client.get('/api/v1/error')).rejects.toStrictEqual( - new UnhandledError(StatusCode.InternalServerError, 'Internal Server Error'), - ) - }) - - it('exposes http post endpoint', async () => { - const client = new HttpClient({ baseUrl: `http://127.0.0.1:${port}`, logger: getLoggerMock().mock }) - const content = { some: 'content' } - await expect(client.post('/api/v1/post', content)).resolves.toMatchObject({ payload: content }) - }) - - it('exposes http patch endpoint', async () => { - const client = new HttpClient({ baseUrl: `http://127.0.0.1:${port}`, logger: getLoggerMock().mock }) - const content = { some: 'content' } - await expect(client.patch('/api/v1/patch', content)).resolves.toMatchObject({ payload: content }) - }) - - it('exposes http put endpoint', async () => { - const client = new HttpClient({ baseUrl: `http://127.0.0.1:${port}`, logger: getLoggerMock().mock }) - const content = { some: 'content' } - await expect(client.put('/api/v1/put', content)).resolves.toMatchObject({ payload: content }) - }) - - it('exposes http delete endpoint', async () => { - const client = new HttpClient({ baseUrl: `http://127.0.0.1:${port}`, logger: getLoggerMock().mock }) - await expect(client.delete('/api/v1/delete')).resolves.toBeUndefined() - }) - - it('stops accepting requests after SIGTERM', async () => { - sandbox.stub(process, 'once') - - // Emit the SIGTERM signal to test if the handler is working as expected - process.emit('SIGTERM') - - const client = new HttpClient({ baseUrl: `http://127.0.0.1:${port}`, logger: getLoggerMock().mock }) - await expect(client.get('healthz')).rejects.toStrictEqual( - new UnhandledError(StatusCode.ServiceUnavailable, 'Service Unavailable'), - ) - }) - - it.skip('logs uncaughtException', async () => { - sandbox.stub(process, 'once') - - process.emit('uncaughtException', new UnhandledError(StatusCode.InternalServerError, 'some error')) - expect(logger.stubs.error.getCall(0).args[1]).toBe('unhandled error: some error') - // expect(logger.stubs.error.calledWithMatch('unhandled error: some error')).toBeTruthy() - }) + let logger: ReturnType + let server: ReturnType + let app: ReturnType + let eventBridge: DefaultEventBridge + const disableEndpointExposing = false + + const sandbox = createSandbox() + + const port = 8082 + + beforeAll(async () => { + logger = getLoggerMock(sandbox) + + eventBridge = new DefaultEventBridge({ logger: getLoggerMock().mock }) + await eventBridge.start() + + // set up the service + const theService = await theServiceV1Service.getInstance(eventBridge, { logger: getLoggerMock().mock }) + await theService.start() + + app = getHttpServer({ + healthFn: async () => true, + services: [theService], + hostname: 'localhost', + apiMountPath: '/api', + disableEndpointExposing, + logger: logger.mock, + }) + + server = serve({ + fetch: app.fetch, + port, + }) + }) + + afterEach(() => { + sandbox.reset() + sandbox.restore() + + logger.stubs.info.resetHistory() + logger.stubs.error.resetHistory() + logger.stubs.warn.resetHistory() + logger.stubs.debug.resetHistory() + logger.stubs.fatal.resetHistory() + }) + + afterAll(async () => { + await eventBridge.destroy() + + const s = server as any + if (s.closeAllConnections) { + await s.closeAllConnections() + } + + await server.close() + }) + + it('returns healthz', async () => { + const client = new HttpClient({ baseUrl: `http://127.0.0.1:${port}` }) + + await expect(client.get('healthz')).resolves.toMatchObject({ + status: 200, + message: 'ok', + }) + }) + + it('exposes http get endpoint', async () => { + const client = new HttpClient({ baseUrl: `http://127.0.0.1:${port}`, logger: getLoggerMock().mock }) + await expect(client.get('/api/v1/ping', { query: { required: 'true' } })).resolves.toMatchObject({ ping: true }) + }) + + it('returns a error on invalid query parameter', async () => { + const client = new HttpClient({ baseUrl: `http://127.0.0.1:${port}`, logger: getLoggerMock().mock }) + await expect(client.get('/api/v1/ping')).rejects.toStrictEqual( + new UnhandledError(StatusCode.BadRequest, 'Bad Request'), + ) + }) + + it('has a 404 handling', async () => { + const client = new HttpClient({ baseUrl: `http://127.0.0.1:${port}`, logger: getLoggerMock().mock }) + await expect(client.get('/api/v1/unknown')).rejects.toStrictEqual( + new UnhandledError(StatusCode.NotFound, 'Not Found'), + ) + }) + + it('returns a error if command returns error', async () => { + const client = new HttpClient({ baseUrl: `http://127.0.0.1:${port}`, logger: getLoggerMock().mock }) + await expect(client.get('/api/v1/error')).rejects.toStrictEqual( + new UnhandledError(StatusCode.InternalServerError, 'Internal Server Error'), + ) + }) + + it('exposes http post endpoint', async () => { + const client = new HttpClient({ baseUrl: `http://127.0.0.1:${port}`, logger: getLoggerMock().mock }) + const content = { some: 'content' } + await expect(client.post('/api/v1/post', content)).resolves.toMatchObject({ payload: content }) + }) + + it('exposes http patch endpoint', async () => { + const client = new HttpClient({ baseUrl: `http://127.0.0.1:${port}`, logger: getLoggerMock().mock }) + const content = { some: 'content' } + await expect(client.patch('/api/v1/patch', content)).resolves.toMatchObject({ payload: content }) + }) + + it('exposes http put endpoint', async () => { + const client = new HttpClient({ baseUrl: `http://127.0.0.1:${port}`, logger: getLoggerMock().mock }) + const content = { some: 'content' } + await expect(client.put('/api/v1/put', content)).resolves.toMatchObject({ payload: content }) + }) + + it('exposes http delete endpoint', async () => { + const client = new HttpClient({ baseUrl: `http://127.0.0.1:${port}`, logger: getLoggerMock().mock }) + await expect(client.delete('/api/v1/delete')).resolves.toBeUndefined() + }) + + it('stops accepting requests after SIGTERM', async () => { + sandbox.stub(process, 'once') + + // Emit the SIGTERM signal to test if the handler is working as expected + process.emit('SIGTERM') + + const client = new HttpClient({ baseUrl: `http://127.0.0.1:${port}`, logger: getLoggerMock().mock }) + await expect(client.get('healthz')).rejects.toStrictEqual( + new UnhandledError(StatusCode.ServiceUnavailable, 'Service Unavailable'), + ) + }) + + it.skip('logs uncaughtException', async () => { + sandbox.stub(process, 'once') + + process.emit('uncaughtException', new UnhandledError(StatusCode.InternalServerError, 'some error')) + expect(logger.stubs.error.getCall(0).args[1]).toBe('unhandled error: some error') + // expect(logger.stubs.error.calledWithMatch('unhandled error: some error')).toBeTruthy() + }) }) diff --git a/packages/k8s-sdk/test/service/theService/generalTheServiceServiceInfo.ts b/packages/k8s-sdk/test/service/theService/generalTheServiceServiceInfo.ts index 45737f772..d4dc4a13a 100644 --- a/packages/k8s-sdk/test/service/theService/generalTheServiceServiceInfo.ts +++ b/packages/k8s-sdk/test/service/theService/generalTheServiceServiceInfo.ts @@ -1,6 +1,6 @@ import type { ServiceInfoType } from '@purista/core' export const generalTheServiceServiceInfo: Omit = { - serviceName: 'TheService', - serviceDescription: 'a example service', + serviceName: 'TheService', + serviceDescription: 'a example service', } diff --git a/packages/k8s-sdk/test/service/theService/v1/command/delete/deleteCommandBuilder.ts b/packages/k8s-sdk/test/service/theService/v1/command/delete/deleteCommandBuilder.ts index 073757a35..3c9e39068 100644 --- a/packages/k8s-sdk/test/service/theService/v1/command/delete/deleteCommandBuilder.ts +++ b/packages/k8s-sdk/test/service/theService/v1/command/delete/deleteCommandBuilder.ts @@ -1,14 +1,14 @@ import { theServiceServiceBuilder } from '../../theServiceServiceBuilder.js' import { - theServiceV1DeleteInputParameterSchema, - theServiceV1DeleteInputPayloadSchema, - theServiceV1DeleteOutputPayloadSchema, + theServiceV1DeleteInputParameterSchema, + theServiceV1DeleteInputPayloadSchema, + theServiceV1DeleteOutputPayloadSchema, } from './schema.js' export const deleteCommandBuilder = theServiceServiceBuilder - .getCommandBuilder('delete', 'provide a dummy command') - .addPayloadSchema(theServiceV1DeleteInputPayloadSchema) - .addParameterSchema(theServiceV1DeleteInputParameterSchema) - .addOutputSchema(theServiceV1DeleteOutputPayloadSchema) - .exposeAsHttpEndpoint('DELETE', 'delete') - .setCommandFunction(async function (_context, _payload, _parameter) {}) + .getCommandBuilder('delete', 'provide a dummy command') + .addPayloadSchema(theServiceV1DeleteInputPayloadSchema) + .addParameterSchema(theServiceV1DeleteInputParameterSchema) + .addOutputSchema(theServiceV1DeleteOutputPayloadSchema) + .exposeAsHttpEndpoint('DELETE', 'delete') + .setCommandFunction(async function (_context, _payload, _parameter) {}) diff --git a/packages/k8s-sdk/test/service/theService/v1/command/delete/schema.ts b/packages/k8s-sdk/test/service/theService/v1/command/delete/schema.ts index 98a542b73..0fd7a0ae9 100644 --- a/packages/k8s-sdk/test/service/theService/v1/command/delete/schema.ts +++ b/packages/k8s-sdk/test/service/theService/v1/command/delete/schema.ts @@ -3,7 +3,7 @@ import { z } from 'zod' // define the input parameters export const theServiceV1DeleteInputParameterSchema = extendApi(z.object({}), { - title: 'delete input parameter schema', + title: 'delete input parameter schema', }) // define the input payload @@ -11,5 +11,5 @@ export const theServiceV1DeleteInputPayloadSchema = extendApi(z.any(), { title: // define the output payload export const theServiceV1DeleteOutputPayloadSchema = extendApi(z.void(), { - title: 'put output payload schema', + title: 'put output payload schema', }) diff --git a/packages/k8s-sdk/test/service/theService/v1/command/delete/types.ts b/packages/k8s-sdk/test/service/theService/v1/command/delete/types.ts index f6d611a99..61a4bfdc9 100644 --- a/packages/k8s-sdk/test/service/theService/v1/command/delete/types.ts +++ b/packages/k8s-sdk/test/service/theService/v1/command/delete/types.ts @@ -1,9 +1,9 @@ import type { z } from 'zod' import type { - theServiceV1DeleteInputParameterSchema, - theServiceV1DeleteInputPayloadSchema, - theServiceV1DeleteOutputPayloadSchema, + theServiceV1DeleteInputParameterSchema, + theServiceV1DeleteInputPayloadSchema, + theServiceV1DeleteOutputPayloadSchema, } from './schema.js' export type TheServiceV1DeleteInputParameter = z.input diff --git a/packages/k8s-sdk/test/service/theService/v1/command/error/errorCommandBuilder.ts b/packages/k8s-sdk/test/service/theService/v1/command/error/errorCommandBuilder.ts index 6a86f1f32..16c5ff1da 100644 --- a/packages/k8s-sdk/test/service/theService/v1/command/error/errorCommandBuilder.ts +++ b/packages/k8s-sdk/test/service/theService/v1/command/error/errorCommandBuilder.ts @@ -1,16 +1,16 @@ import { theServiceServiceBuilder } from '../../theServiceServiceBuilder.js' import { - theServiceV1ErrorInputParameterSchema, - theServiceV1ErrorInputPayloadSchema, - theServiceV1ErrorOutputPayloadSchema, + theServiceV1ErrorInputParameterSchema, + theServiceV1ErrorInputPayloadSchema, + theServiceV1ErrorOutputPayloadSchema, } from './schema.js' export const errorCommandBuilder = theServiceServiceBuilder - .getCommandBuilder('error', 'provide a dummy command') - .addPayloadSchema(theServiceV1ErrorInputPayloadSchema) - .addParameterSchema(theServiceV1ErrorInputParameterSchema) - .addOutputSchema(theServiceV1ErrorOutputPayloadSchema) - .exposeAsHttpEndpoint('GET', 'error') - .setCommandFunction(async function (_context, _payload, _parameter) { - throw new Error('some error') - }) + .getCommandBuilder('error', 'provide a dummy command') + .addPayloadSchema(theServiceV1ErrorInputPayloadSchema) + .addParameterSchema(theServiceV1ErrorInputParameterSchema) + .addOutputSchema(theServiceV1ErrorOutputPayloadSchema) + .exposeAsHttpEndpoint('GET', 'error') + .setCommandFunction(async function (_context, _payload, _parameter) { + throw new Error('some error') + }) diff --git a/packages/k8s-sdk/test/service/theService/v1/command/error/schema.ts b/packages/k8s-sdk/test/service/theService/v1/command/error/schema.ts index 463b3a027..a1cb48dd6 100644 --- a/packages/k8s-sdk/test/service/theService/v1/command/error/schema.ts +++ b/packages/k8s-sdk/test/service/theService/v1/command/error/schema.ts @@ -9,5 +9,5 @@ export const theServiceV1ErrorInputPayloadSchema = extendApi(z.undefined(), { ti // define the output payload export const theServiceV1ErrorOutputPayloadSchema = extendApi(z.object({ error: z.boolean() }), { - title: 'error output payload schema', + title: 'error output payload schema', }) diff --git a/packages/k8s-sdk/test/service/theService/v1/command/error/types.ts b/packages/k8s-sdk/test/service/theService/v1/command/error/types.ts index 2dfd509ea..0981793e8 100644 --- a/packages/k8s-sdk/test/service/theService/v1/command/error/types.ts +++ b/packages/k8s-sdk/test/service/theService/v1/command/error/types.ts @@ -1,9 +1,9 @@ import type { z } from 'zod' import type { - theServiceV1ErrorInputParameterSchema, - theServiceV1ErrorInputPayloadSchema, - theServiceV1ErrorOutputPayloadSchema, + theServiceV1ErrorInputParameterSchema, + theServiceV1ErrorInputPayloadSchema, + theServiceV1ErrorOutputPayloadSchema, } from './schema.js' export type TheServiceV1ErrorInputParameter = z.input diff --git a/packages/k8s-sdk/test/service/theService/v1/command/patch/patchCommandBuilder.ts b/packages/k8s-sdk/test/service/theService/v1/command/patch/patchCommandBuilder.ts index 271a8a269..21ccce6de 100644 --- a/packages/k8s-sdk/test/service/theService/v1/command/patch/patchCommandBuilder.ts +++ b/packages/k8s-sdk/test/service/theService/v1/command/patch/patchCommandBuilder.ts @@ -1,18 +1,18 @@ import { theServiceServiceBuilder } from '../../theServiceServiceBuilder.js' import { - theServiceV1PatchInputParameterSchema, - theServiceV1PatchInputPayloadSchema, - theServiceV1PatchOutputPayloadSchema, + theServiceV1PatchInputParameterSchema, + theServiceV1PatchInputPayloadSchema, + theServiceV1PatchOutputPayloadSchema, } from './schema.js' export const patchCommandBuilder = theServiceServiceBuilder - .getCommandBuilder('patch', 'provide a dummy command') - .addPayloadSchema(theServiceV1PatchInputPayloadSchema) - .addParameterSchema(theServiceV1PatchInputParameterSchema) - .addOutputSchema(theServiceV1PatchOutputPayloadSchema) - .exposeAsHttpEndpoint('PATCH', 'patch') - .setCommandFunction(async function (_context, payload, _parameter) { - return { - payload, - } - }) + .getCommandBuilder('patch', 'provide a dummy command') + .addPayloadSchema(theServiceV1PatchInputPayloadSchema) + .addParameterSchema(theServiceV1PatchInputParameterSchema) + .addOutputSchema(theServiceV1PatchOutputPayloadSchema) + .exposeAsHttpEndpoint('PATCH', 'patch') + .setCommandFunction(async function (_context, payload, _parameter) { + return { + payload, + } + }) diff --git a/packages/k8s-sdk/test/service/theService/v1/command/patch/schema.ts b/packages/k8s-sdk/test/service/theService/v1/command/patch/schema.ts index cbf9ec2bb..368d58059 100644 --- a/packages/k8s-sdk/test/service/theService/v1/command/patch/schema.ts +++ b/packages/k8s-sdk/test/service/theService/v1/command/patch/schema.ts @@ -9,5 +9,5 @@ export const theServiceV1PatchInputPayloadSchema = extendApi(z.any(), { title: ' // define the output payload export const theServiceV1PatchOutputPayloadSchema = extendApi(z.any(), { - title: 'patch output payload schema', + title: 'patch output payload schema', }) diff --git a/packages/k8s-sdk/test/service/theService/v1/command/patch/types.ts b/packages/k8s-sdk/test/service/theService/v1/command/patch/types.ts index bdaa86252..cfbd5f28d 100644 --- a/packages/k8s-sdk/test/service/theService/v1/command/patch/types.ts +++ b/packages/k8s-sdk/test/service/theService/v1/command/patch/types.ts @@ -1,9 +1,9 @@ import type { z } from 'zod' import type { - theServiceV1PatchInputParameterSchema, - theServiceV1PatchInputPayloadSchema, - theServiceV1PatchOutputPayloadSchema, + theServiceV1PatchInputParameterSchema, + theServiceV1PatchInputPayloadSchema, + theServiceV1PatchOutputPayloadSchema, } from './schema.js' export type TheServiceV1PatchInputParameter = z.input diff --git a/packages/k8s-sdk/test/service/theService/v1/command/ping/pingCommandBuilder.ts b/packages/k8s-sdk/test/service/theService/v1/command/ping/pingCommandBuilder.ts index d40d31eb9..f16f2c7bc 100644 --- a/packages/k8s-sdk/test/service/theService/v1/command/ping/pingCommandBuilder.ts +++ b/packages/k8s-sdk/test/service/theService/v1/command/ping/pingCommandBuilder.ts @@ -1,19 +1,19 @@ import { theServiceServiceBuilder } from '../../theServiceServiceBuilder.js' import { - theServiceV1PingInputParameterSchema, - theServiceV1PingInputPayloadSchema, - theServiceV1PingOutputPayloadSchema, + theServiceV1PingInputParameterSchema, + theServiceV1PingInputPayloadSchema, + theServiceV1PingOutputPayloadSchema, } from './schema.js' export const pingCommandBuilder = theServiceServiceBuilder - .getCommandBuilder('ping', 'provide a dummy command') - .addPayloadSchema(theServiceV1PingInputPayloadSchema) - .addParameterSchema(theServiceV1PingInputParameterSchema) - .addOutputSchema(theServiceV1PingOutputPayloadSchema) - .exposeAsHttpEndpoint('GET', 'ping') - .addQueryParameters({ name: 'param', required: false }, { required: true, name: 'required' }) - .setCommandFunction(async function (_context, _payload, _parameter) { - return { - ping: true, - } - }) + .getCommandBuilder('ping', 'provide a dummy command') + .addPayloadSchema(theServiceV1PingInputPayloadSchema) + .addParameterSchema(theServiceV1PingInputParameterSchema) + .addOutputSchema(theServiceV1PingOutputPayloadSchema) + .exposeAsHttpEndpoint('GET', 'ping') + .addQueryParameters({ name: 'param', required: false }, { required: true, name: 'required' }) + .setCommandFunction(async function (_context, _payload, _parameter) { + return { + ping: true, + } + }) diff --git a/packages/k8s-sdk/test/service/theService/v1/command/ping/schema.ts b/packages/k8s-sdk/test/service/theService/v1/command/ping/schema.ts index b20b855b7..a49cf1bdd 100644 --- a/packages/k8s-sdk/test/service/theService/v1/command/ping/schema.ts +++ b/packages/k8s-sdk/test/service/theService/v1/command/ping/schema.ts @@ -3,11 +3,11 @@ import { z } from 'zod' // define the input parameters export const theServiceV1PingInputParameterSchema = extendApi( - z.object({ - param: z.string().optional(), - required: z.string(), - }), - { title: 'ping input parameter schema' }, + z.object({ + param: z.string().optional(), + required: z.string(), + }), + { title: 'ping input parameter schema' }, ) // define the input payload @@ -15,5 +15,5 @@ export const theServiceV1PingInputPayloadSchema = extendApi(z.undefined(), { tit // define the output payload export const theServiceV1PingOutputPayloadSchema = extendApi(z.object({ ping: z.boolean() }), { - title: 'ping output payload schema', + title: 'ping output payload schema', }) diff --git a/packages/k8s-sdk/test/service/theService/v1/command/ping/types.ts b/packages/k8s-sdk/test/service/theService/v1/command/ping/types.ts index babb58f4b..1aacbf07a 100644 --- a/packages/k8s-sdk/test/service/theService/v1/command/ping/types.ts +++ b/packages/k8s-sdk/test/service/theService/v1/command/ping/types.ts @@ -1,9 +1,9 @@ import type { z } from 'zod' import type { - theServiceV1PingInputParameterSchema, - theServiceV1PingInputPayloadSchema, - theServiceV1PingOutputPayloadSchema, + theServiceV1PingInputParameterSchema, + theServiceV1PingInputPayloadSchema, + theServiceV1PingOutputPayloadSchema, } from './schema.js' export type TheServiceV1PingInputParameter = z.input diff --git a/packages/k8s-sdk/test/service/theService/v1/command/post/postCommandBuilder.ts b/packages/k8s-sdk/test/service/theService/v1/command/post/postCommandBuilder.ts index c4fab4486..f282b53a2 100644 --- a/packages/k8s-sdk/test/service/theService/v1/command/post/postCommandBuilder.ts +++ b/packages/k8s-sdk/test/service/theService/v1/command/post/postCommandBuilder.ts @@ -1,18 +1,18 @@ import { theServiceServiceBuilder } from '../../theServiceServiceBuilder.js' import { - theServiceV1PostInputParameterSchema, - theServiceV1PostInputPayloadSchema, - theServiceV1PostOutputPayloadSchema, + theServiceV1PostInputParameterSchema, + theServiceV1PostInputPayloadSchema, + theServiceV1PostOutputPayloadSchema, } from './schema.js' export const postCommandBuilder = theServiceServiceBuilder - .getCommandBuilder('post', 'provide a dummy command') - .addPayloadSchema(theServiceV1PostInputPayloadSchema) - .addParameterSchema(theServiceV1PostInputParameterSchema) - .addOutputSchema(theServiceV1PostOutputPayloadSchema) - .exposeAsHttpEndpoint('POST', 'post') - .setCommandFunction(async function (_context, payload, _parameter) { - return { - payload, - } - }) + .getCommandBuilder('post', 'provide a dummy command') + .addPayloadSchema(theServiceV1PostInputPayloadSchema) + .addParameterSchema(theServiceV1PostInputParameterSchema) + .addOutputSchema(theServiceV1PostOutputPayloadSchema) + .exposeAsHttpEndpoint('POST', 'post') + .setCommandFunction(async function (_context, payload, _parameter) { + return { + payload, + } + }) diff --git a/packages/k8s-sdk/test/service/theService/v1/command/post/schema.ts b/packages/k8s-sdk/test/service/theService/v1/command/post/schema.ts index a03b3526d..2c2ed82bd 100644 --- a/packages/k8s-sdk/test/service/theService/v1/command/post/schema.ts +++ b/packages/k8s-sdk/test/service/theService/v1/command/post/schema.ts @@ -9,5 +9,5 @@ export const theServiceV1PostInputPayloadSchema = extendApi(z.any(), { title: 'p // define the output payload export const theServiceV1PostOutputPayloadSchema = extendApi(z.any(), { - title: 'post output payload schema', + title: 'post output payload schema', }) diff --git a/packages/k8s-sdk/test/service/theService/v1/command/post/types.ts b/packages/k8s-sdk/test/service/theService/v1/command/post/types.ts index 862670f8e..7f78d4ad7 100644 --- a/packages/k8s-sdk/test/service/theService/v1/command/post/types.ts +++ b/packages/k8s-sdk/test/service/theService/v1/command/post/types.ts @@ -1,9 +1,9 @@ import type { z } from 'zod' import type { - theServiceV1PostInputParameterSchema, - theServiceV1PostInputPayloadSchema, - theServiceV1PostOutputPayloadSchema, + theServiceV1PostInputParameterSchema, + theServiceV1PostInputPayloadSchema, + theServiceV1PostOutputPayloadSchema, } from './schema.js' export type TheServiceV1PostInputParameter = z.input diff --git a/packages/k8s-sdk/test/service/theService/v1/command/put/putCommandBuilder.ts b/packages/k8s-sdk/test/service/theService/v1/command/put/putCommandBuilder.ts index bb1556790..7595098c1 100644 --- a/packages/k8s-sdk/test/service/theService/v1/command/put/putCommandBuilder.ts +++ b/packages/k8s-sdk/test/service/theService/v1/command/put/putCommandBuilder.ts @@ -1,18 +1,18 @@ import { theServiceServiceBuilder } from '../../theServiceServiceBuilder.js' import { - theServiceV1PutInputParameterSchema, - theServiceV1PutInputPayloadSchema, - theServiceV1PutOutputPayloadSchema, + theServiceV1PutInputParameterSchema, + theServiceV1PutInputPayloadSchema, + theServiceV1PutOutputPayloadSchema, } from './schema.js' export const putCommandBuilder = theServiceServiceBuilder - .getCommandBuilder('put', 'provide a dummy command') - .addPayloadSchema(theServiceV1PutInputPayloadSchema) - .addParameterSchema(theServiceV1PutInputParameterSchema) - .addOutputSchema(theServiceV1PutOutputPayloadSchema) - .exposeAsHttpEndpoint('PUT', 'put') - .setCommandFunction(async function (_context, payload, _parameter) { - return { - payload, - } - }) + .getCommandBuilder('put', 'provide a dummy command') + .addPayloadSchema(theServiceV1PutInputPayloadSchema) + .addParameterSchema(theServiceV1PutInputParameterSchema) + .addOutputSchema(theServiceV1PutOutputPayloadSchema) + .exposeAsHttpEndpoint('PUT', 'put') + .setCommandFunction(async function (_context, payload, _parameter) { + return { + payload, + } + }) diff --git a/packages/k8s-sdk/test/service/theService/v1/command/put/schema.ts b/packages/k8s-sdk/test/service/theService/v1/command/put/schema.ts index 77cb18c67..661fea101 100644 --- a/packages/k8s-sdk/test/service/theService/v1/command/put/schema.ts +++ b/packages/k8s-sdk/test/service/theService/v1/command/put/schema.ts @@ -9,5 +9,5 @@ export const theServiceV1PutInputPayloadSchema = extendApi(z.any(), { title: 'pu // define the output payload export const theServiceV1PutOutputPayloadSchema = extendApi(z.any(), { - title: 'put output payload schema', + title: 'put output payload schema', }) diff --git a/packages/k8s-sdk/test/service/theService/v1/command/put/types.ts b/packages/k8s-sdk/test/service/theService/v1/command/put/types.ts index bbb0c339c..5414c02a1 100644 --- a/packages/k8s-sdk/test/service/theService/v1/command/put/types.ts +++ b/packages/k8s-sdk/test/service/theService/v1/command/put/types.ts @@ -1,9 +1,9 @@ import type { z } from 'zod' import type { - theServiceV1PutInputParameterSchema, - theServiceV1PutInputPayloadSchema, - theServiceV1PutOutputPayloadSchema, + theServiceV1PutInputParameterSchema, + theServiceV1PutInputPayloadSchema, + theServiceV1PutOutputPayloadSchema, } from './schema.js' export type TheServiceV1PutInputParameter = z.input diff --git a/packages/k8s-sdk/test/service/theService/v1/theServiceServiceBuilder.ts b/packages/k8s-sdk/test/service/theService/v1/theServiceServiceBuilder.ts index 2b393ff0c..2f61f4814 100644 --- a/packages/k8s-sdk/test/service/theService/v1/theServiceServiceBuilder.ts +++ b/packages/k8s-sdk/test/service/theService/v1/theServiceServiceBuilder.ts @@ -5,12 +5,12 @@ import { generalTheServiceServiceInfo } from '../generalTheServiceServiceInfo.js import { theServiceServiceV1ConfigSchema } from './theServiceServiceConfig.js' export const theServiceServiceInfo: ServiceInfoType = { - serviceVersion: '1', - ...generalTheServiceServiceInfo, + serviceVersion: '1', + ...generalTheServiceServiceInfo, } // create a service builder instance and assign service config schema and default config. -export const theServiceServiceBuilder = new ServiceBuilder(theServiceServiceInfo) - .setConfigSchema(theServiceServiceV1ConfigSchema) - .setDefaultConfig({}) +export const theServiceServiceBuilder = new ServiceBuilder(theServiceServiceInfo).setConfigSchema( + theServiceServiceV1ConfigSchema, +) diff --git a/packages/k8s-sdk/test/service/theService/v1/theServiceV1Service.ts b/packages/k8s-sdk/test/service/theService/v1/theServiceV1Service.ts index a937ad836..128a373d5 100644 --- a/packages/k8s-sdk/test/service/theService/v1/theServiceV1Service.ts +++ b/packages/k8s-sdk/test/service/theService/v1/theServiceV1Service.ts @@ -13,16 +13,16 @@ import { theServiceServiceBuilder } from './theServiceServiceBuilder.js' // other service config should be done in ./theServiceServiceBuilder.ts file const commandDefinitions: CommandDefinitionList = [ - pingCommandBuilder.getDefinition(), - postCommandBuilder.getDefinition(), - putCommandBuilder.getDefinition(), - patchCommandBuilder.getDefinition(), - deleteCommandBuilder.getDefinition(), - errorCommandBuilder.getDefinition(), + pingCommandBuilder.getDefinition(), + postCommandBuilder.getDefinition(), + putCommandBuilder.getDefinition(), + patchCommandBuilder.getDefinition(), + deleteCommandBuilder.getDefinition(), + errorCommandBuilder.getDefinition(), ] const subscriptionDefinitions: SubscriptionDefinitionList = [] export const theServiceV1Service = theServiceServiceBuilder - .addCommandDefinition(...commandDefinitions) - .addSubscriptionDefinition(...subscriptionDefinitions) + .addCommandDefinition(...commandDefinitions) + .addSubscriptionDefinition(...subscriptionDefinitions) diff --git a/packages/k8s-sdk/tsconfig.json b/packages/k8s-sdk/tsconfig.json index 42dac6999..a2f689e2f 100644 --- a/packages/k8s-sdk/tsconfig.json +++ b/packages/k8s-sdk/tsconfig.json @@ -1,21 +1,13 @@ { - "extends": "../../tsconfig.json", - "compilerOptions": { - "outDir": "./dist", - "declaration": true, - "sourceMap": false, - "declarationMap": true, - "types": [ - "vitest/globals", - "node" - ] - }, - "exclude": [ - "./**/*.d.ts" - ], - - "include": [ - "./src/**/*", - "./test/*", - ], -} \ No newline at end of file + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "./dist", + "declaration": true, + "sourceMap": false, + "declarationMap": true, + "types": ["vitest/globals", "node"] + }, + "exclude": ["./**/*.d.ts"], + + "include": ["./src/**/*", "./test/*"] +} diff --git a/packages/k8s-sdk/typedoc.json b/packages/k8s-sdk/typedoc.json index 355bf0f98..71c4b2283 100644 --- a/packages/k8s-sdk/typedoc.json +++ b/packages/k8s-sdk/typedoc.json @@ -1,6 +1,5 @@ { - - "extends": ["../../typedoc.base.json"], - "entryPoints": ["src/index.ts"], - "tsconfig": "./tsconfig.json" -} \ No newline at end of file + "extends": ["../../typedoc.base.json"], + "entryPoints": ["src/index.ts"], + "tsconfig": "./tsconfig.json" +} diff --git a/packages/mqttbridge/jsr.json b/packages/mqttbridge/jsr.json new file mode 100644 index 000000000..489308481 --- /dev/null +++ b/packages/mqttbridge/jsr.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://jsr.io/schema/config-file.v1.json", + "name": "@purista/mqttbridge", + "version": "1.11.0", + "description": "MQTT eventbridge for PURISTA backend framework", + "keywords": ["purista", "mqtt", "typescript", "javascript"], + "exports": "./dist/esm/index.js", + "publish": { + "include": ["dist/**/*.js", "dist/**/*.d.ts", "README.md", "package.json"], + "exclude": [ + "src", + ".github", + ".vscode", + ".zed", + "!dist", + "!dist/**/*.js", + "!dist/**/*.d.ts", + ".tshy", + ".tshy-build", + "vendor", + "docs", + "typedoc.json", + "..eslintcache", + ".npmignore" + ] + } +} diff --git a/packages/mqttbridge/package.json b/packages/mqttbridge/package.json index f24a39f04..bb42809dc 100644 --- a/packages/mqttbridge/package.json +++ b/packages/mqttbridge/package.json @@ -1,68 +1,65 @@ { - "name": "@purista/mqttbridge", - "version": "1.11.0", - "description": "MQTT eventbridge for PURISTA backend framework", - "homepage": "https://purista.dev", - "repository": { - "type": "git", - "url": "git@github.com:sebastianwessel/purista.git" - }, - "author": "Sebastian Wessel", - "license": "ISC", - "type": "module", - "main": "./dist/commonjs/index.js", - "exports": { - "./package.json": "./package.json", - ".": { - "import": { - "types": "./dist/esm/index.d.ts", - "default": "./dist/esm/index.js" - }, - "require": { - "types": "./dist/commonjs/index.d.ts", - "default": "./dist/commonjs/index.js" - } - } - }, - "files": [ - "dist/**/*" - ], - "publishConfig": { - "access": "public" - }, - "engines": { - "node": ">=18.15" - }, - "scripts": { - "lint": "eslint . --ext .ts,.json --cache . --fix", - "test": "vitest", - "build": "rimraf dist && tshy" - }, - "tshy": { - "exclude": [ - "src/**/*.test.ts" - ], - "exports": { - "./package.json": "./package.json", - ".": "./src/index.ts" - } - }, - "devDependencies": { - "@types/node": "^20.11.17", - "@types/sinon": "^17.0.3", - "@types/ws": "^8.5.10", - "sinon": "^17.0.1", - "tshy": "^1.11.1", - "vitest": "^1.3.0" - }, - "dependencies": { - "@opentelemetry/api": "^1.7.0", - "@opentelemetry/resources": "^1.19.0", - "@opentelemetry/sdk-trace-node": "^1.19.0", - "@opentelemetry/semantic-conventions": "^1.19.0", - "@purista/core": "*", - "mqtt": "^5.3.5" - }, - "peerDependenciesMeta": {}, - "types": "./dist/commonjs/index.d.ts" + "name": "@purista/mqttbridge", + "version": "1.11.0", + "description": "MQTT eventbridge for PURISTA backend framework", + "homepage": "https://purista.dev", + "repository": { + "type": "git", + "url": "git@github.com:puristajs/purista.git" + }, + "author": "Sebastian Wessel", + "license": "ISC", + "type": "module", + "main": "./dist/commonjs/index.js", + "exports": { + "./package.json": "./package.json", + ".": { + "import": { + "types": "./dist/esm/index.d.ts", + "default": "./dist/esm/index.js" + }, + "require": { + "types": "./dist/commonjs/index.d.ts", + "default": "./dist/commonjs/index.js" + } + } + }, + "files": ["dist/**/*"], + "publishConfig": { + "access": "public" + }, + "engines": { + "node": ">=18.15" + }, + "scripts": { + "lint": "npx @biomejs/biome check --write", + "test": "vitest", + "build": "rimraf dist && tshy" + }, + "tshy": { + "exclude": ["src/**/*.test.ts"], + "exports": { + "./package.json": "./package.json", + ".": "./src/index.ts" + } + }, + "devDependencies": { + "@types/node": "^22.5.1", + "@types/sinon": "^17.0.3", + "@types/ws": "^8.5.12", + "sinon": "^19.0.2", + "tshy": "^3.0.2", + "vitest": "^3.0.4" + }, + "dependencies": { + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/resources": "^1.26.0", + "@opentelemetry/sdk-trace-node": "^1.26.0", + "@opentelemetry/semantic-conventions": "^1.27.0", + "@purista/core": "*", + "mqtt": "^5.9.0" + }, + "peerDependenciesMeta": {}, + "types": "./dist/commonjs/index.d.ts", + "module": "./dist/esm/index.js" } diff --git a/packages/mqttbridge/src/MqttEventBridge.ts b/packages/mqttbridge/src/MqttEventBridge.ts index 0789af264..f40d31bbb 100644 --- a/packages/mqttbridge/src/MqttEventBridge.ts +++ b/packages/mqttbridge/src/MqttEventBridge.ts @@ -1,36 +1,36 @@ import { SpanKind } from '@opentelemetry/api' import type { - BrokerHeaderCommandMsg, - BrokerHeaderCustomMsg, - Command, - CommandDefinitionMetadataBase, - CommandErrorResponse, - CommandSuccessResponse, - CustomMessage, - DefinitionEventBridgeConfig, - EBMessage, - EBMessageAddress, - EBMessageId, - EventBridge, - EventBridgeConfig, - PendigInvocation, - Subscription, + BrokerHeaderCommandMsg, + BrokerHeaderCustomMsg, + Command, + CommandDefinitionMetadataBase, + CommandErrorResponse, + CommandSuccessResponse, + CustomMessage, + DefinitionEventBridgeConfig, + EBMessage, + EBMessageAddress, + EBMessageId, + EventBridge, + EventBridgeConfig, + PendigInvocation, + Subscription, } from '@purista/core' import { - createInfoMessage, - deserializeOtp, - EBMessageType, - EventBridgeBaseClass, - EventBridgeEventNames, - getNewCorrelationId, - getNewEBMessageId, - getNewInstanceId, - isCommandResponse, - PuristaSpanName, - PuristaSpanTag, - serializeOtp, - StatusCode, - UnhandledError, + EBMessageType, + EventBridgeBaseClass, + EventBridgeEventNames, + PuristaSpanName, + PuristaSpanTag, + StatusCode, + UnhandledError, + createInfoMessage, + deserializeOtp, + getNewCorrelationId, + getNewEBMessageId, + getNewInstanceId, + isCommandResponse, + serializeOtp, } from '@purista/core' import type { IClientSubscribeOptions, IPublishPacket, MqttClient } from 'mqtt' import { connectAsync } from 'mqtt' @@ -40,12 +40,12 @@ import { getCommandHandler, getSubscriptionHandler, handleCommandResponse } from import { msToSec } from './msToSec.impl.js' import { serializeOtpToMqtt } from './serializeOtpToMqtt.impl.js' import { - getCommandResponseSubscriptionTopic, - getCommandSubscriptionTopic, - getSharedTopicName, - getSubscriptionTopic, - getTopicName, - TopicRouter, + TopicRouter, + getCommandResponseSubscriptionTopic, + getCommandSubscriptionTopic, + getSharedTopicName, + getSubscriptionTopic, + getTopicName, } from './topic/index.js' import type { MqttBridgeConfig } from './types/index.js' @@ -64,323 +64,325 @@ import type { MqttBridgeConfig } from './types/index.js' * @group Event bridge */ export class MqttBridge extends EventBridgeBaseClass implements EventBridge { - private healthy = false - private ready = false - public client: MqttClient | undefined - public pendingInvocations = new Map() - private router = new TopicRouter() - - constructor(config?: EventBridgeConfig>) { - const conf = { - ...getDefaultMqttBridgeConfig(), - ...config, - clientId: config?.clientId ?? config?.instanceId ?? getNewInstanceId(), - } - super('MqttBridge', conf) - } - - async start() { - this.client = await connectAsync(this.config) - - this.client.on('connect', () => { - this.emit(EventBridgeEventNames.EventbridgeConnected) - }) - - this.client.on('error', (err: Error) => { - this.emit(EventBridgeEventNames.EventbridgeError, err) - }) - - this.client.on('disconnect', () => { - this.emit(EventBridgeEventNames.EventbridgeDisconnected) - }) - - this.client.on('reconnect', () => { - this.emit(EventBridgeEventNames.EventbridgeReconnecting) - }) - - const topic = getCommandResponseSubscriptionTopic.bind(this)() - const subscriptionIdentifier = this.router.add(topic, handleCommandResponse) - await this.client.subscribeAsync(topic, { - qos: this.config.qosCommand, - properties: { subscriptionIdentifier }, - }) - - this.client.on('message', (topic: string, payload: Buffer, packet: IPublishPacket) => { - const handler = this.router.match(topic, packet.properties?.subscriptionIdentifier as number | undefined) - if (!handler.length) { - const err = new UnhandledError(StatusCode.InternalServerError, 'received message for unknown topic ', { - topic: packet.topic, - id: packet.properties?.subscriptionIdentifier, - }) - this.logger.error({ err }, err.message) - } - - let content: EBMessage - try { - content = JSON.parse(payload.toString()) - } catch (err) { - this.logger.error({ err }, 'unable to parse received MQTT message') - return - } - - handler.forEach((fn) => fn.bind(this)(content, packet)) - }) - } - - async emitMessage( - message: Omit, - contentType = 'application/json', - contentEncoding = 'utf-8', - ): Promise> { - const context = deserializeOtp(this.logger, message.otp) - - const name = isCommandResponse(message as EBMessage) - ? PuristaSpanName.EventBridgeCommandResponseSent - : PuristaSpanName.EventBridgeEmitMessage - - return this.startActiveSpan(name, { kind: SpanKind.PRODUCER }, context, async (span) => { - const msg = Object.freeze({ - ...message, - sender: { - ...message.sender, - instanceId: this.instanceId, - }, - id: getNewEBMessageId(), - timestamp: Date.now(), - traceId: message.traceId, - otp: serializeOtp(), - contentType, - contentEncoding, - }) as EBMessage - - span.setAttribute(PuristaSpanTag.SenderServiceName, msg.sender.serviceName) - span.setAttribute(PuristaSpanTag.SenderServiceVersion, msg.sender.serviceVersion) - span.setAttribute(PuristaSpanTag.SenderServiceTarget, msg.sender.serviceTarget) - - if (msg.eventName) { - span.addEvent(msg.eventName) - } - - const userProperties: BrokerHeaderCustomMsg = serializeOtpToMqtt({ - messageType: msg.messageType, - senderServiceName: msg.sender.serviceName, - senderServiceVersion: msg.sender.serviceVersion, - senderServiceTarget: msg.sender.serviceTarget, - senderInstanceId: msg.sender.instanceId, - }) - - if (msg.eventName) { - userProperties.eventName = msg.eventName - } - - if (msg.principalId) { - userProperties.principalId = msg.principalId - } - - if (msg.tenantId) { - userProperties.tenantId = msg.tenantId - } - - const topic = getTopicName.bind(this)(msg) - await this.client?.publishAsync(topic, JSON.stringify(msg), { - qos: this.config.qoSSubscription, - properties: { - contentType: 'application/json', - userProperties, - messageExpiryInterval: this.config.defaultMessageExpiryInterval, - }, - }) - - return msg as Readonly - }) - } - - async isReady() { - return !!this.client?.connected - } - - async isHealthy() { - return !!this.client?.connected - } - - async invoke( - input: Omit, - commandTimeout: number = this.defaultCommandTimeout, - ): Promise { - const context = deserializeOtp(this.logger, input.otp) - return this.startActiveSpan( - PuristaSpanName.EventBridgeInvokeCommand, - { kind: SpanKind.PRODUCER }, - context, - async (span) => { - const correlationId = getNewCorrelationId() - - const command: Command = Object.freeze({ - ...input, - sender: { - ...input.sender, - instanceId: this.instanceId, - }, - id: getNewEBMessageId(), - correlationId, - timestamp: Date.now(), - messageType: EBMessageType.Command, - traceId: input.traceId, - otp: serializeOtp(), - }) - - const log = this.logger.getChildLogger({ ...span.spanContext(), customTraceId: command.traceId }) - - const removeFromPending = () => { - this.pendingInvocations.delete(correlationId) - } - - const executionPromise = new Promise((resolve, reject) => { - const timeout = setTimeout(() => { - const err = new UnhandledError( - StatusCode.GatewayTimeout, - 'invocation timed out', - undefined, - command.traceId, - ) - log.warn({ err }) - rejectFn(err) - }, commandTimeout) - - const resolveFn = (successPayload: T) => { - clearTimeout(timeout) - removeFromPending() - resolve(successPayload) - } - - const rejectFn = (err: unknown) => { - clearTimeout(timeout) - removeFromPending() - reject(err) - } - - this.pendingInvocations.set(command.correlationId, { - resolve: resolveFn, - reject: rejectFn, - }) - }) - - span.setAttribute(PuristaSpanTag.SenderServiceName, command.sender.serviceName) - span.setAttribute(PuristaSpanTag.SenderServiceVersion, command.sender.serviceVersion) - span.setAttribute(PuristaSpanTag.SenderServiceTarget, command.sender.serviceTarget) - span.setAttribute(PuristaSpanTag.ReceiverServiceName, command.receiver.serviceName) - span.setAttribute(PuristaSpanTag.ReceiverServiceVersion, command.receiver.serviceVersion) - span.setAttribute(PuristaSpanTag.ReceiverServiceTarget, command.receiver.serviceTarget) - - const userProperties: BrokerHeaderCommandMsg = serializeOtpToMqtt({ - messageType: command.messageType, - senderServiceName: command.sender.serviceName, - senderServiceVersion: command.sender.serviceVersion, - senderServiceTarget: command.sender.serviceTarget, - senderInstanceId: command.sender.instanceId, - receiverServiceName: command.receiver.serviceName, - receiverServiceVersion: command.receiver.serviceVersion, - receiverServiceTarget: command.receiver.serviceTarget, - }) - - if (command.eventName) { - userProperties.eventName = command.eventName - } - - if (command.receiver.instanceId) { - userProperties.receiverInstanceId = command.receiver.instanceId - } - - if (command.principalId) { - userProperties.principalId = command.principalId - } - - if (command.tenantId) { - userProperties.tenantId = command.tenantId - } - - const topic = getTopicName.bind(this)(command) - - await this.client?.publishAsync(topic, JSON.stringify(command), { - // if event name is set use the largest QOS - qos: command.eventName - ? this.config.qoSSubscription > this.config.qosCommand - ? this.config.qoSSubscription - : this.config.qosCommand - : this.config.qosCommand, - properties: { - messageExpiryInterval: command.eventName - ? this.config.defaultMessageExpiryInterval - : msToSec(commandTimeout), - contentType: 'application/json', - userProperties, - correlationData: Buffer.from(command.correlationId), - }, - }) - - return executionPromise - }, - ) - } - - async registerCommand( - address: EBMessageAddress, - cb: (message: Command) => Promise, - metadata: CommandDefinitionMetadataBase, - eventBridgeConfig: DefinitionEventBridgeConfig, - ): Promise { - const topic = getSharedTopicName.bind(this)(getCommandSubscriptionTopic.bind(this)(address)) - const subscriptionIdentifier = this.router.add(topic, getCommandHandler(address, cb, metadata, eventBridgeConfig)) - await this.client?.subscribeAsync(topic, { - qos: this.config.qosCommand, - properties: { subscriptionIdentifier }, - }) - - const info = createInfoMessage( - EBMessageType.InfoServiceFunctionAdded, - { ...address, instanceId: this.instanceId }, - { payload: metadata }, - ) - await this.emitMessage(info) - - return topic - } - - async unregisterCommand(address: EBMessageAddress): Promise { - const topic = getSharedTopicName.bind(this)(getCommandSubscriptionTopic.bind(this)(address)) - await this.client?.unsubscribeAsync(topic) - this.router.remove(topic) - } - - async registerSubscription( - subscription: Subscription, - cb: (message: EBMessage) => Promise | undefined>, - ): Promise { - const opts: IClientSubscribeOptions = { qos: this.config.qoSSubscription, properties: {} } - - let topic = getSubscriptionTopic.bind(this)(subscription) - - const shared = subscription.eventBridgeConfig.shared - - if (shared) { - topic = getSharedTopicName.bind(this)(topic) - } - - const subscriptionIdentifier = this.router.add(topic, getSubscriptionHandler(subscription, cb)) - - opts.properties = { - ...opts.properties, - subscriptionIdentifier, - } - - await this.client?.subscribeAsync(topic, opts) - - return topic - } - - async unregisterSubscription(_address: EBMessageAddress): Promise {} - - async destroy() { - this.client?.end(true) - } + private healthy = false + private ready = false + public client: MqttClient | undefined + public pendingInvocations = new Map() + private router = new TopicRouter() + + constructor(config?: EventBridgeConfig>) { + const conf = { + ...getDefaultMqttBridgeConfig(), + ...config, + clientId: config?.clientId ?? config?.instanceId ?? getNewInstanceId(), + } + super('MqttBridge', conf) + } + + async start() { + this.client = await connectAsync(this.config) + + this.client.on('connect', () => { + this.emit(EventBridgeEventNames.EventbridgeConnected) + }) + + this.client.on('error', (err: Error) => { + this.emit(EventBridgeEventNames.EventbridgeError, err) + }) + + this.client.on('disconnect', () => { + this.emit(EventBridgeEventNames.EventbridgeDisconnected) + }) + + this.client.on('reconnect', () => { + this.emit(EventBridgeEventNames.EventbridgeReconnecting) + }) + + const topic = getCommandResponseSubscriptionTopic.bind(this)() + const subscriptionIdentifier = this.router.add(topic, handleCommandResponse) + await this.client.subscribeAsync(topic, { + qos: this.config.qosCommand, + properties: { subscriptionIdentifier }, + }) + + this.client.on('message', (topic: string, payload: Buffer, packet: IPublishPacket) => { + const handler = this.router.match(topic, packet.properties?.subscriptionIdentifier as number | undefined) + if (!handler.length) { + const err = new UnhandledError(StatusCode.InternalServerError, 'received message for unknown topic ', { + topic: packet.topic, + id: packet.properties?.subscriptionIdentifier, + }) + this.logger.error({ err }, err.message) + } + + let content: EBMessage + try { + content = JSON.parse(payload.toString()) + } catch (err) { + this.logger.error({ err }, 'unable to parse received MQTT message') + return + } + + for (const fn of handler) { + fn.bind(this)(content, packet) + } + }) + } + + async emitMessage( + message: Omit, + contentType = 'application/json', + contentEncoding = 'utf-8', + ): Promise> { + const context = deserializeOtp(this.logger, message.otp) + + const name = isCommandResponse(message as EBMessage) + ? PuristaSpanName.EventBridgeCommandResponseSent + : PuristaSpanName.EventBridgeEmitMessage + + return this.startActiveSpan(name, { kind: SpanKind.PRODUCER }, context, async span => { + const msg = Object.freeze({ + ...message, + sender: { + ...message.sender, + instanceId: this.instanceId, + }, + id: getNewEBMessageId(), + timestamp: Date.now(), + traceId: message.traceId, + otp: serializeOtp(), + contentType, + contentEncoding, + }) as EBMessage + + span.setAttribute(PuristaSpanTag.SenderServiceName, msg.sender.serviceName) + span.setAttribute(PuristaSpanTag.SenderServiceVersion, msg.sender.serviceVersion) + span.setAttribute(PuristaSpanTag.SenderServiceTarget, msg.sender.serviceTarget) + + if (msg.eventName) { + span.addEvent(msg.eventName) + } + + const userProperties: BrokerHeaderCustomMsg = serializeOtpToMqtt({ + messageType: msg.messageType, + senderServiceName: msg.sender.serviceName, + senderServiceVersion: msg.sender.serviceVersion, + senderServiceTarget: msg.sender.serviceTarget, + senderInstanceId: msg.sender.instanceId, + }) + + if (msg.eventName) { + userProperties.eventName = msg.eventName + } + + if (msg.principalId) { + userProperties.principalId = msg.principalId + } + + if (msg.tenantId) { + userProperties.tenantId = msg.tenantId + } + + const topic = getTopicName.bind(this)(msg) + await this.client?.publishAsync(topic, JSON.stringify(msg), { + qos: this.config.qoSSubscription, + properties: { + contentType: 'application/json', + userProperties, + messageExpiryInterval: this.config.defaultMessageExpiryInterval, + }, + }) + + return msg as Readonly + }) + } + + async isReady() { + return !!this.client?.connected + } + + async isHealthy() { + return !!this.client?.connected + } + + async invoke( + input: Omit, + commandTimeout: number = this.defaultCommandTimeout, + ): Promise { + const context = deserializeOtp(this.logger, input.otp) + return this.startActiveSpan( + PuristaSpanName.EventBridgeInvokeCommand, + { kind: SpanKind.PRODUCER }, + context, + async span => { + const correlationId = getNewCorrelationId() + + const command: Command = Object.freeze({ + ...input, + sender: { + ...input.sender, + instanceId: this.instanceId, + }, + id: getNewEBMessageId(), + correlationId, + timestamp: Date.now(), + messageType: EBMessageType.Command, + traceId: input.traceId, + otp: serializeOtp(), + }) + + const log = this.logger.getChildLogger({ ...span.spanContext(), customTraceId: command.traceId }) + + const removeFromPending = () => { + this.pendingInvocations.delete(correlationId) + } + + const executionPromise = new Promise((resolve, reject) => { + const timeout = setTimeout(() => { + const err = new UnhandledError( + StatusCode.GatewayTimeout, + 'invocation timed out', + undefined, + command.traceId, + ) + log.warn({ err }) + rejectFn(err) + }, commandTimeout) + + const resolveFn = (successPayload: T) => { + clearTimeout(timeout) + removeFromPending() + resolve(successPayload) + } + + const rejectFn = (err: unknown) => { + clearTimeout(timeout) + removeFromPending() + reject(err) + } + + this.pendingInvocations.set(command.correlationId, { + resolve: resolveFn, + reject: rejectFn, + }) + }) + + span.setAttribute(PuristaSpanTag.SenderServiceName, command.sender.serviceName) + span.setAttribute(PuristaSpanTag.SenderServiceVersion, command.sender.serviceVersion) + span.setAttribute(PuristaSpanTag.SenderServiceTarget, command.sender.serviceTarget) + span.setAttribute(PuristaSpanTag.ReceiverServiceName, command.receiver.serviceName) + span.setAttribute(PuristaSpanTag.ReceiverServiceVersion, command.receiver.serviceVersion) + span.setAttribute(PuristaSpanTag.ReceiverServiceTarget, command.receiver.serviceTarget) + + const userProperties: BrokerHeaderCommandMsg = serializeOtpToMqtt({ + messageType: command.messageType, + senderServiceName: command.sender.serviceName, + senderServiceVersion: command.sender.serviceVersion, + senderServiceTarget: command.sender.serviceTarget, + senderInstanceId: command.sender.instanceId, + receiverServiceName: command.receiver.serviceName, + receiverServiceVersion: command.receiver.serviceVersion, + receiverServiceTarget: command.receiver.serviceTarget, + }) + + if (command.eventName) { + userProperties.eventName = command.eventName + } + + if (command.receiver.instanceId) { + userProperties.receiverInstanceId = command.receiver.instanceId + } + + if (command.principalId) { + userProperties.principalId = command.principalId + } + + if (command.tenantId) { + userProperties.tenantId = command.tenantId + } + + const topic = getTopicName.bind(this)(command) + + await this.client?.publishAsync(topic, JSON.stringify(command), { + // if event name is set use the largest QOS + qos: command.eventName + ? this.config.qoSSubscription > this.config.qosCommand + ? this.config.qoSSubscription + : this.config.qosCommand + : this.config.qosCommand, + properties: { + messageExpiryInterval: command.eventName + ? this.config.defaultMessageExpiryInterval + : msToSec(commandTimeout), + contentType: 'application/json', + userProperties, + correlationData: Buffer.from(command.correlationId), + }, + }) + + return executionPromise + }, + ) + } + + async registerCommand( + address: EBMessageAddress, + cb: (message: Command) => Promise, + metadata: CommandDefinitionMetadataBase, + eventBridgeConfig: DefinitionEventBridgeConfig, + ): Promise { + const topic = getSharedTopicName.bind(this)(getCommandSubscriptionTopic.bind(this)(address)) + const subscriptionIdentifier = this.router.add(topic, getCommandHandler(address, cb, metadata, eventBridgeConfig)) + await this.client?.subscribeAsync(topic, { + qos: this.config.qosCommand, + properties: { subscriptionIdentifier }, + }) + + const info = createInfoMessage( + EBMessageType.InfoServiceFunctionAdded, + { ...address, instanceId: this.instanceId }, + { payload: metadata }, + ) + await this.emitMessage(info) + + return topic + } + + async unregisterCommand(address: EBMessageAddress): Promise { + const topic = getSharedTopicName.bind(this)(getCommandSubscriptionTopic.bind(this)(address)) + await this.client?.unsubscribeAsync(topic) + this.router.remove(topic) + } + + async registerSubscription( + subscription: Subscription, + cb: (message: EBMessage) => Promise | undefined>, + ): Promise { + const opts: IClientSubscribeOptions = { qos: this.config.qoSSubscription, properties: {} } + + let topic = getSubscriptionTopic.bind(this)(subscription) + + const shared = subscription.eventBridgeConfig.shared + + if (shared) { + topic = getSharedTopicName.bind(this)(topic) + } + + const subscriptionIdentifier = this.router.add(topic, getSubscriptionHandler(subscription, cb)) + + opts.properties = { + ...opts.properties, + subscriptionIdentifier, + } + + await this.client?.subscribeAsync(topic, opts) + + return topic + } + + async unregisterSubscription(_address: EBMessageAddress): Promise {} + + async destroy() { + this.client?.end(true) + } } diff --git a/packages/mqttbridge/src/deserializeOtpFromMqtt.impl.ts b/packages/mqttbridge/src/deserializeOtpFromMqtt.impl.ts index 6798c0579..fb189e12a 100644 --- a/packages/mqttbridge/src/deserializeOtpFromMqtt.impl.ts +++ b/packages/mqttbridge/src/deserializeOtpFromMqtt.impl.ts @@ -4,10 +4,10 @@ import { deserializeOtp } from '@purista/core' import type { UserProperties } from 'mqtt-packet' export const deserializeOtpFromMqtt = (logger: Logger, message: EBMessage, userProperties: UserProperties = {}) => { - // try to use mqtt user property first - if (userProperties['traceparent']) { - return propagation.extract(context.active(), userProperties) - } + // try to use mqtt user property first + if (userProperties.traceparent) { + return propagation.extract(context.active(), userProperties) + } - return deserializeOtp(logger, message.otp) + return deserializeOtp(logger, message.otp) } diff --git a/packages/mqttbridge/src/getDefaultMqttBridgeConfig.impl.ts b/packages/mqttbridge/src/getDefaultMqttBridgeConfig.impl.ts index 8eb3648ca..1f2047378 100644 --- a/packages/mqttbridge/src/getDefaultMqttBridgeConfig.impl.ts +++ b/packages/mqttbridge/src/getDefaultMqttBridgeConfig.impl.ts @@ -3,30 +3,30 @@ import type { MqttBridgeConfig } from './types/index.js' const SECONDS_PER_DAY = 86_400 export const getDefaultMqttBridgeConfig = (): MqttBridgeConfig => { - return { - topicPrefix: 'purista', - shareTopicName: 'sharedpurista', - shareTopicPrefix: '$share', - emptyTopicPartString: '__none__', + return { + topicPrefix: 'purista', + shareTopicName: 'sharedpurista', + shareTopicPrefix: '$share', + emptyTopicPartString: '__none__', - qosCommand: 1, - qoSSubscription: 1, + qosCommand: 1, + qoSSubscription: 1, - defaultSessionExpiryInterval: 30 * SECONDS_PER_DAY, - defaultMessageExpiryInterval: 30 * SECONDS_PER_DAY, + defaultSessionExpiryInterval: 30 * SECONDS_PER_DAY, + defaultMessageExpiryInterval: 30 * SECONDS_PER_DAY, - host: 'localhost', - port: 1883, - protocol: 'mqtt', - protocolVersion: 5, - clean: true, - resubscribe: true, - allowRetries: true, + host: 'localhost', + port: 1883, + protocol: 'mqtt', + protocolVersion: 5, + clean: true, + resubscribe: true, + allowRetries: true, - keepalive: 10, - reschedulePings: true, - protocolId: 'MQTT', - reconnectPeriod: 1_000, - connectTimeout: 30 * 1_000, - } + keepalive: 10, + reschedulePings: true, + protocolId: 'MQTT', + reconnectPeriod: 1_000, + connectTimeout: 30 * 1_000, + } } diff --git a/packages/mqttbridge/src/handler/getCommandHandler.impl.ts b/packages/mqttbridge/src/handler/getCommandHandler.impl.ts index 50ce1c706..78d98fe45 100644 --- a/packages/mqttbridge/src/handler/getCommandHandler.impl.ts +++ b/packages/mqttbridge/src/handler/getCommandHandler.impl.ts @@ -1,23 +1,23 @@ import { SpanKind, SpanStatusCode } from '@opentelemetry/api' import type { - BrokerHeaderCommandResponseMsg, - Command, - CommandDefinitionMetadataBase, - CommandErrorResponse, - CommandSuccessResponse, - DefinitionEventBridgeConfig, - EBMessage, - EBMessageAddress, + BrokerHeaderCommandResponseMsg, + Command, + CommandDefinitionMetadataBase, + CommandErrorResponse, + CommandSuccessResponse, + DefinitionEventBridgeConfig, + EBMessage, + EBMessageAddress, } from '@purista/core' import { - deserializeOtp, - EventBridgeEventNames, - isCommand, - PuristaSpanName, - PuristaSpanTag, - serializeOtp, - StatusCode, - UnhandledError, + EventBridgeEventNames, + PuristaSpanName, + PuristaSpanTag, + StatusCode, + UnhandledError, + deserializeOtp, + isCommand, + serializeOtp, } from '@purista/core' import { deserializeOtpFromMqtt } from '../deserializeOtpFromMqtt.impl.js' @@ -27,111 +27,111 @@ import { getTopicName } from '../topic/index.js' import type { IncomingMessageFunction } from '../types/index.js' export const getCommandHandler = ( - address: EBMessageAddress, - cb: (message: Command) => Promise, - _metadata: CommandDefinitionMetadataBase, - _eventBridgeConfig: DefinitionEventBridgeConfig, + address: EBMessageAddress, + cb: (message: Command) => Promise, + _metadata: CommandDefinitionMetadataBase, + _eventBridgeConfig: DefinitionEventBridgeConfig, ) => { - const handleCommand: IncomingMessageFunction = async function (command: EBMessage, packet) { - const context = deserializeOtpFromMqtt(this.logger, command, packet.properties?.userProperties) - return this.startActiveSpan( - PuristaSpanName.EventBridgeCommandReceived, - { kind: SpanKind.CONSUMER }, - context, - async (span) => { - const log = this.logger.getChildLogger({ ...span.spanContext(), customTraceId: command.traceId }) - try { - if (!isCommand(command)) { - const err = new UnhandledError(StatusCode.InternalServerError, 'expected a command message') - log.error({ err }, err.message) - span.setStatus({ - code: SpanStatusCode.ERROR, - message: err.message, - }) - span.recordException(err) - this.emit(EventBridgeEventNames.EventbridgeError, err) - return - } + const handleCommand: IncomingMessageFunction = async function (command: EBMessage, packet) { + const context = deserializeOtpFromMqtt(this.logger, command, packet.properties?.userProperties) + return this.startActiveSpan( + PuristaSpanName.EventBridgeCommandReceived, + { kind: SpanKind.CONSUMER }, + context, + async span => { + const log = this.logger.getChildLogger({ ...span.spanContext(), customTraceId: command.traceId }) + try { + if (!isCommand(command)) { + const err = new UnhandledError(StatusCode.InternalServerError, 'expected a command message') + log.error({ err }, err.message) + span.setStatus({ + code: SpanStatusCode.ERROR, + message: err.message, + }) + span.recordException(err) + this.emit(EventBridgeEventNames.EventbridgeError, err) + return + } - const result = await cb(command) + const result = await cb(command) - const returnContext = deserializeOtp(log, result.otp) - return this.startActiveSpan( - PuristaSpanName.EventBridgeCommandResponseSent, - { kind: SpanKind.PRODUCER }, - returnContext, - async (subSpan) => { - const responseMessage = { - ...result, - sender: { - ...result.sender, - instanceId: this.instanceId, - }, - otp: result.otp ?? serializeOtp(), - } + const returnContext = deserializeOtp(log, result.otp) + return this.startActiveSpan( + PuristaSpanName.EventBridgeCommandResponseSent, + { kind: SpanKind.PRODUCER }, + returnContext, + async subSpan => { + const responseMessage = { + ...result, + sender: { + ...result.sender, + instanceId: this.instanceId, + }, + otp: result.otp ?? serializeOtp(), + } - subSpan.setAttribute(PuristaSpanTag.SenderServiceName, responseMessage.sender.serviceName) - subSpan.setAttribute(PuristaSpanTag.SenderServiceVersion, responseMessage.sender.serviceVersion) - subSpan.setAttribute(PuristaSpanTag.SenderServiceTarget, responseMessage.sender.serviceTarget) + subSpan.setAttribute(PuristaSpanTag.SenderServiceName, responseMessage.sender.serviceName) + subSpan.setAttribute(PuristaSpanTag.SenderServiceVersion, responseMessage.sender.serviceVersion) + subSpan.setAttribute(PuristaSpanTag.SenderServiceTarget, responseMessage.sender.serviceTarget) - if (responseMessage.eventName) { - subSpan.addEvent(responseMessage.eventName) - } + if (responseMessage.eventName) { + subSpan.addEvent(responseMessage.eventName) + } - const userProperties: BrokerHeaderCommandResponseMsg = serializeOtpToMqtt({ - messageType: responseMessage.messageType, - senderServiceName: responseMessage.sender.serviceName, - senderServiceVersion: responseMessage.sender.serviceVersion, - senderServiceTarget: responseMessage.sender.serviceTarget, - senderInstanceId: responseMessage.sender.instanceId, - receiverServiceName: responseMessage.receiver.serviceName, - receiverServiceVersion: responseMessage.receiver.serviceVersion, - receiverServiceTarget: responseMessage.receiver.serviceTarget, - receiverInstanceId: responseMessage.receiver.instanceId, - }) + const userProperties: BrokerHeaderCommandResponseMsg = serializeOtpToMqtt({ + messageType: responseMessage.messageType, + senderServiceName: responseMessage.sender.serviceName, + senderServiceVersion: responseMessage.sender.serviceVersion, + senderServiceTarget: responseMessage.sender.serviceTarget, + senderInstanceId: responseMessage.sender.instanceId, + receiverServiceName: responseMessage.receiver.serviceName, + receiverServiceVersion: responseMessage.receiver.serviceVersion, + receiverServiceTarget: responseMessage.receiver.serviceTarget, + receiverInstanceId: responseMessage.receiver.instanceId, + }) - if (responseMessage.eventName) { - userProperties.eventName = responseMessage.eventName - } + if (responseMessage.eventName) { + userProperties.eventName = responseMessage.eventName + } - if (responseMessage.principalId) { - userProperties.principalId = responseMessage.principalId - } + if (responseMessage.principalId) { + userProperties.principalId = responseMessage.principalId + } - if (responseMessage.tenantId) { - userProperties.tenantId = responseMessage.tenantId - } + if (responseMessage.tenantId) { + userProperties.tenantId = responseMessage.tenantId + } - // emit the message 1st time as direct response - const responseTopic = getTopicName.bind(this)(responseMessage) - await this.client?.publish(responseTopic, JSON.stringify(responseMessage), { - qos: this.config.qosCommand, - properties: { - messageExpiryInterval: responseMessage.eventName - ? msToSec(this.config.defaultMessageExpiryInterval) - : this.config.defaultCommandTimeout, - contentType: 'application/json', - userProperties, - correlationData: Buffer.from(responseMessage.correlationId), - }, - }) - }, - ) - } catch (error) { - const err = new UnhandledError(StatusCode.InternalServerError, 'Failed to consume command response message', { - error, - }) - span.setStatus({ - code: SpanStatusCode.ERROR, - message: err.message, - }) - span.recordException(err) - this.emit(EventBridgeEventNames.EventbridgeError, err) - log.error({ err }, 'Failed to consume command response message') - } - }, - ) - } + // emit the message 1st time as direct response + const responseTopic = getTopicName.bind(this)(responseMessage) + await this.client?.publish(responseTopic, JSON.stringify(responseMessage), { + qos: this.config.qosCommand, + properties: { + messageExpiryInterval: responseMessage.eventName + ? msToSec(this.config.defaultMessageExpiryInterval) + : this.config.defaultCommandTimeout, + contentType: 'application/json', + userProperties, + correlationData: Buffer.from(responseMessage.correlationId), + }, + }) + }, + ) + } catch (error) { + const err = new UnhandledError(StatusCode.InternalServerError, 'Failed to consume command response message', { + error, + }) + span.setStatus({ + code: SpanStatusCode.ERROR, + message: err.message, + }) + span.recordException(err) + this.emit(EventBridgeEventNames.EventbridgeError, err) + log.error({ err }, 'Failed to consume command response message') + } + }, + ) + } - return handleCommand + return handleCommand } diff --git a/packages/mqttbridge/src/handler/getSubscriptionHandler.impl.ts b/packages/mqttbridge/src/handler/getSubscriptionHandler.impl.ts index 2d9b8b391..7609e2c45 100644 --- a/packages/mqttbridge/src/handler/getSubscriptionHandler.impl.ts +++ b/packages/mqttbridge/src/handler/getSubscriptionHandler.impl.ts @@ -1,13 +1,13 @@ import { SpanKind, SpanStatusCode } from '@opentelemetry/api' import type { BrokerHeaderCustomMsg, CustomMessage, EBMessage, Subscription } from '@purista/core' import { - deserializeOtp, - EventBridgeEventNames, - PuristaSpanName, - PuristaSpanTag, - serializeOtp, - StatusCode, - UnhandledError, + EventBridgeEventNames, + PuristaSpanName, + PuristaSpanTag, + StatusCode, + UnhandledError, + deserializeOtp, + serializeOtp, } from '@purista/core' import { deserializeOtpFromMqtt } from '../deserializeOtpFromMqtt.impl.js' @@ -16,93 +16,93 @@ import { getTopicName } from '../topic/index.js' import type { IncomingMessageFunction } from '../types/index.js' export const getSubscriptionHandler = ( - _subscription: Subscription, - cb: (message: EBMessage) => Promise | undefined>, + _subscription: Subscription, + cb: (message: EBMessage) => Promise | undefined>, ) => { - const handler: IncomingMessageFunction = async function (message: EBMessage, packet) { - const context = deserializeOtpFromMqtt(this.logger, message, packet.properties?.userProperties) - return this.startActiveSpan( - PuristaSpanName.EventBridgeCommandReceived, - { kind: SpanKind.CONSUMER }, - context, - async (span) => { - const log = this.logger.getChildLogger({ ...span.spanContext(), customTraceId: message.traceId }) + const handler: IncomingMessageFunction = async function (message: EBMessage, packet) { + const context = deserializeOtpFromMqtt(this.logger, message, packet.properties?.userProperties) + return this.startActiveSpan( + PuristaSpanName.EventBridgeCommandReceived, + { kind: SpanKind.CONSUMER }, + context, + async span => { + const log = this.logger.getChildLogger({ ...span.spanContext(), customTraceId: message.traceId }) - try { - const result = await cb(message) + try { + const result = await cb(message) - if (!result) { - return - } + if (!result) { + return + } - const returnContext = deserializeOtp(log, result.otp) - return this.startActiveSpan( - PuristaSpanName.EventBridgeCommandResponseSent, - { kind: SpanKind.PRODUCER }, - returnContext, - async (subSpan) => { - const responseMessage = { - ...result, - sender: { - ...result.sender, - instanceId: this.instanceId, - }, - otp: serializeOtp(), - } + const returnContext = deserializeOtp(log, result.otp) + return this.startActiveSpan( + PuristaSpanName.EventBridgeCommandResponseSent, + { kind: SpanKind.PRODUCER }, + returnContext, + async subSpan => { + const responseMessage = { + ...result, + sender: { + ...result.sender, + instanceId: this.instanceId, + }, + otp: serializeOtp(), + } - subSpan.setAttribute(PuristaSpanTag.SenderServiceName, responseMessage.sender.serviceName) - subSpan.setAttribute(PuristaSpanTag.SenderServiceVersion, responseMessage.sender.serviceVersion) - subSpan.setAttribute(PuristaSpanTag.SenderServiceTarget, responseMessage.sender.serviceTarget) + subSpan.setAttribute(PuristaSpanTag.SenderServiceName, responseMessage.sender.serviceName) + subSpan.setAttribute(PuristaSpanTag.SenderServiceVersion, responseMessage.sender.serviceVersion) + subSpan.setAttribute(PuristaSpanTag.SenderServiceTarget, responseMessage.sender.serviceTarget) - if (!responseMessage.eventName) { - return - } + if (!responseMessage.eventName) { + return + } - subSpan.addEvent(responseMessage.eventName) + subSpan.addEvent(responseMessage.eventName) - const userProperties: BrokerHeaderCustomMsg = serializeOtpToMqtt({ - messageType: responseMessage.messageType, - senderServiceName: responseMessage.sender.serviceName, - senderServiceVersion: responseMessage.sender.serviceVersion, - senderServiceTarget: responseMessage.sender.serviceTarget, - senderInstanceId: responseMessage.sender.instanceId, - eventName: responseMessage.eventName, - }) + const userProperties: BrokerHeaderCustomMsg = serializeOtpToMqtt({ + messageType: responseMessage.messageType, + senderServiceName: responseMessage.sender.serviceName, + senderServiceVersion: responseMessage.sender.serviceVersion, + senderServiceTarget: responseMessage.sender.serviceTarget, + senderInstanceId: responseMessage.sender.instanceId, + eventName: responseMessage.eventName, + }) - if (responseMessage.principalId) { - userProperties.principalId = responseMessage.principalId - } + if (responseMessage.principalId) { + userProperties.principalId = responseMessage.principalId + } - if (responseMessage.tenantId) { - userProperties.tenantId = responseMessage.tenantId - } + if (responseMessage.tenantId) { + userProperties.tenantId = responseMessage.tenantId + } - const topic = getTopicName.bind(this)(responseMessage as EBMessage) - await this.client?.publish(topic, JSON.stringify(responseMessage), { - qos: this.config.qoSSubscription, - properties: { - messageExpiryInterval: this.config.defaultMessageExpiryInterval, - contentType: 'application/json', - userProperties, - }, - }) - }, - ) - } catch (error) { - const err = new UnhandledError(StatusCode.InternalServerError, 'Failed to consume subscription message', { - error, - }) - span.setStatus({ - code: SpanStatusCode.ERROR, - message: err.message, - }) - span.recordException(err) - this.emit(EventBridgeEventNames.EventbridgeError, err) - log.error({ err }, 'Failed to consume subscription message') - } - }, - ) - } + const topic = getTopicName.bind(this)(responseMessage as EBMessage) + await this.client?.publish(topic, JSON.stringify(responseMessage), { + qos: this.config.qoSSubscription, + properties: { + messageExpiryInterval: this.config.defaultMessageExpiryInterval, + contentType: 'application/json', + userProperties, + }, + }) + }, + ) + } catch (error) { + const err = new UnhandledError(StatusCode.InternalServerError, 'Failed to consume subscription message', { + error, + }) + span.setStatus({ + code: SpanStatusCode.ERROR, + message: err.message, + }) + span.recordException(err) + this.emit(EventBridgeEventNames.EventbridgeError, err) + log.error({ err }, 'Failed to consume subscription message') + } + }, + ) + } - return handler + return handler } diff --git a/packages/mqttbridge/src/handler/handleCommandResponse.impl.ts b/packages/mqttbridge/src/handler/handleCommandResponse.impl.ts index 72ea9084a..322bd3bf4 100644 --- a/packages/mqttbridge/src/handler/handleCommandResponse.impl.ts +++ b/packages/mqttbridge/src/handler/handleCommandResponse.impl.ts @@ -1,74 +1,74 @@ import { SpanKind, SpanStatusCode } from '@opentelemetry/api' import { - EventBridgeEventNames, - HandledError, - isCommandErrorResponse, - isCommandResponse, - isCommandSuccessResponse, - PuristaSpanName, - StatusCode, - UnhandledError, + EventBridgeEventNames, + HandledError, + PuristaSpanName, + StatusCode, + UnhandledError, + isCommandErrorResponse, + isCommandResponse, + isCommandSuccessResponse, } from '@purista/core' import { deserializeOtpFromMqtt } from '../deserializeOtpFromMqtt.impl.js' import type { IncomingMessageFunction } from '../types/index.js' export const handleCommandResponse: IncomingMessageFunction = async function (message, packet) { - const context = deserializeOtpFromMqtt(this.logger, message, packet.properties?.userProperties) - return this.startActiveSpan( - PuristaSpanName.EventBridgeCommandResponseReceived, - { kind: SpanKind.CONSUMER }, - context, - async (span) => { - const log = this.logger.getChildLogger({ ...span.spanContext(), customTraceId: message.traceId }) + const context = deserializeOtpFromMqtt(this.logger, message, packet.properties?.userProperties) + return this.startActiveSpan( + PuristaSpanName.EventBridgeCommandResponseReceived, + { kind: SpanKind.CONSUMER }, + context, + async span => { + const log = this.logger.getChildLogger({ ...span.spanContext(), customTraceId: message.traceId }) - if (message.eventName) { - span.addEvent(message.eventName) - } + if (message.eventName) { + span.addEvent(message.eventName) + } - if (!isCommandResponse(message)) { - const err = new UnhandledError(StatusCode.InternalServerError, 'the received message is not a command') - log.error({ err }, err.message) - span.setStatus({ - code: SpanStatusCode.ERROR, - message: err.message, - }) - span.recordException(err) - this.emit(EventBridgeEventNames.EventbridgeError, err) - return - } + if (!isCommandResponse(message)) { + const err = new UnhandledError(StatusCode.InternalServerError, 'the received message is not a command') + log.error({ err }, err.message) + span.setStatus({ + code: SpanStatusCode.ERROR, + message: err.message, + }) + span.recordException(err) + this.emit(EventBridgeEventNames.EventbridgeError, err) + return + } - const correlationId = packet.properties?.correlationData?.toString() ?? message.correlationId + const correlationId = packet.properties?.correlationData?.toString() ?? message.correlationId - const invocation = this.pendingInvocations.get(correlationId) + const invocation = this.pendingInvocations.get(correlationId) - if (!invocation) { - const err = new UnhandledError( - StatusCode.InternalServerError, - `received response with invalid correlationId ${correlationId}`, - ) - log.error({ err }, err.message) - span.setStatus({ - code: SpanStatusCode.ERROR, - message: err.message, - }) - span.recordException(err) - this.emit(EventBridgeEventNames.EventbridgeError, err) - return - } + if (!invocation) { + const err = new UnhandledError( + StatusCode.InternalServerError, + `received response with invalid correlationId ${correlationId}`, + ) + log.error({ err }, err.message) + span.setStatus({ + code: SpanStatusCode.ERROR, + message: err.message, + }) + span.recordException(err) + this.emit(EventBridgeEventNames.EventbridgeError, err) + return + } - if (isCommandSuccessResponse(message)) { - invocation.resolve(message.payload) - } else if (isCommandErrorResponse(message)) { - const error = message.isHandledError ? HandledError.fromMessage(message) : UnhandledError.fromMessage(message) - log.error({ err: error }, error.message) - span.recordException(error) - span.setStatus({ - code: SpanStatusCode.ERROR, - message: error.message, - }) - invocation.reject(error) - } - }, - ) + if (isCommandSuccessResponse(message)) { + invocation.resolve(message.payload) + } else if (isCommandErrorResponse(message)) { + const error = message.isHandledError ? HandledError.fromMessage(message) : UnhandledError.fromMessage(message) + log.error({ err: error }, error.message) + span.recordException(error) + span.setStatus({ + code: SpanStatusCode.ERROR, + message: error.message, + }) + invocation.reject(error) + } + }, + ) } diff --git a/packages/mqttbridge/src/index.test.ts b/packages/mqttbridge/src/index.test.ts index f58ce4007..3476fb877 100644 --- a/packages/mqttbridge/src/index.test.ts +++ b/packages/mqttbridge/src/index.test.ts @@ -1,11 +1,11 @@ import { MqttBridge, puristaVersion } from './index.js' describe('exports version', () => { - it('has a version', () => { - expect(puristaVersion).toBeDefined() - }) + it('has a version', () => { + expect(puristaVersion).toBeDefined() + }) - it('exports MqttBridge', () => { - expect(MqttBridge).toBeDefined() - }) + it('exports MqttBridge', () => { + expect(MqttBridge).toBeDefined() + }) }) diff --git a/packages/mqttbridge/src/msToSec.impl.ts b/packages/mqttbridge/src/msToSec.impl.ts index bc69024be..672ac2d29 100644 --- a/packages/mqttbridge/src/msToSec.impl.ts +++ b/packages/mqttbridge/src/msToSec.impl.ts @@ -5,8 +5,8 @@ * @returns rounded value in seconds */ export const msToSec = (ms: number) => { - if (ms === 0) { - return 0 - } - return Math.round(ms / 1000) + if (ms === 0) { + return 0 + } + return Math.round(ms / 1000) } diff --git a/packages/mqttbridge/src/msToSec.test.ts b/packages/mqttbridge/src/msToSec.test.ts index e9054120b..72b325360 100644 --- a/packages/mqttbridge/src/msToSec.test.ts +++ b/packages/mqttbridge/src/msToSec.test.ts @@ -1,23 +1,23 @@ import { msToSec } from './msToSec.impl.js' describe('msToSec', () => { - it('converts milliseconds to seconds', () => { - expect(msToSec(2000)).toBe(2) - }) + it('converts milliseconds to seconds', () => { + expect(msToSec(2000)).toBe(2) + }) - it('rounds milliseconds to seconds', () => { - expect(msToSec(2050)).toBe(2) - }) + it('rounds milliseconds to seconds', () => { + expect(msToSec(2050)).toBe(2) + }) - it('returns 0 on 0', () => { - expect(msToSec(0)).toBe(0) - }) + it('returns 0 on 0', () => { + expect(msToSec(0)).toBe(0) + }) - it('returns 0 on very small values', () => { - expect(msToSec(200)).toBe(0) - }) + it('returns 0 on very small values', () => { + expect(msToSec(200)).toBe(0) + }) - it('returns up on very small values', () => { - expect(msToSec(500)).toBe(1) - }) + it('returns up on very small values', () => { + expect(msToSec(500)).toBe(1) + }) }) diff --git a/packages/mqttbridge/src/serializeOtpToMqtt.impl.ts b/packages/mqttbridge/src/serializeOtpToMqtt.impl.ts index 433c2c17f..1dd298f52 100644 --- a/packages/mqttbridge/src/serializeOtpToMqtt.impl.ts +++ b/packages/mqttbridge/src/serializeOtpToMqtt.impl.ts @@ -1,6 +1,6 @@ import { context, propagation } from '@opentelemetry/api' export const serializeOtpToMqtt = >(serializedContext: T) => { - propagation.inject(context.active(), serializedContext) - return serializedContext + propagation.inject(context.active(), serializedContext) + return serializedContext } diff --git a/packages/mqttbridge/src/topic/TopicRouter.ts b/packages/mqttbridge/src/topic/TopicRouter.ts index 4a25dba1e..75a6270a1 100644 --- a/packages/mqttbridge/src/topic/TopicRouter.ts +++ b/packages/mqttbridge/src/topic/TopicRouter.ts @@ -5,47 +5,47 @@ import type { IncomingMessageFunction } from '../types/index.js' import { isMatchingTopic } from './isMatchingTopic.impl.js' export class TopicRouter { - routes = new Map() - logger: Logger - - counter = 1 - - constructor(logger?: Logger) { - const log = logger ?? initLogger() - this.logger = log.getChildLogger({ name: 'TopicRouter' }) - } - - add(topic: string, fn: IncomingMessageFunction) { - this.counter++ - this.routes.set(this.counter, { topic, fn }) - - this.logger.debug({ topic, count: this.counter }, 'topic added') - return this.counter - } - - remove(topic: string | number) { - if (typeof topic === 'number') { - this.routes.delete(topic) - } - } - - match(topic: string, id?: number): IncomingMessageFunction[] { - const handler: IncomingMessageFunction[] = [] - - if (id) { - const entry = this.routes.get(id) - if (entry) { - handler.push(entry.fn) - } - return handler - } - - this.routes.forEach((entry) => { - if (isMatchingTopic(topic, entry.topic)) { - handler.push(entry.fn) - } - }) - - return handler - } + routes = new Map() + logger: Logger + + counter = 1 + + constructor(logger?: Logger) { + const log = logger ?? initLogger() + this.logger = log.getChildLogger({ name: 'TopicRouter' }) + } + + add(topic: string, fn: IncomingMessageFunction) { + this.counter++ + this.routes.set(this.counter, { topic, fn }) + + this.logger.debug({ topic, count: this.counter }, 'topic added') + return this.counter + } + + remove(topic: string | number) { + if (typeof topic === 'number') { + this.routes.delete(topic) + } + } + + match(topic: string, id?: number): IncomingMessageFunction[] { + const handler: IncomingMessageFunction[] = [] + + if (id) { + const entry = this.routes.get(id) + if (entry) { + handler.push(entry.fn) + } + return handler + } + + for (const [_, entry] of this.routes) { + if (isMatchingTopic(topic, entry.topic)) { + handler.push(entry.fn) + } + } + + return handler + } } diff --git a/packages/mqttbridge/src/topic/getCommandResponseSubscriptionTopic.impl.ts b/packages/mqttbridge/src/topic/getCommandResponseSubscriptionTopic.impl.ts index 373c85d95..27928b3a5 100644 --- a/packages/mqttbridge/src/topic/getCommandResponseSubscriptionTopic.impl.ts +++ b/packages/mqttbridge/src/topic/getCommandResponseSubscriptionTopic.impl.ts @@ -6,19 +6,19 @@ import type { MqttBridge } from '../MqttEventBridge.js' type GetCommandResponseSubscriptionTopicFn = (this: MqttBridge) => string export const getCommandResponseSubscriptionTopic: GetCommandResponseSubscriptionTopicFn = function () { - return join( - this.config.topicPrefix, - '+', - '+', - '+', - '+', - '+', - '+', - '+', - '+', - convertToSnakeCase(this.instanceId as string), - '+', - '+', - '+', - ) + return join( + this.config.topicPrefix, + '+', + '+', + '+', + '+', + '+', + '+', + '+', + '+', + convertToSnakeCase(this.instanceId as string), + '+', + '+', + '+', + ) } diff --git a/packages/mqttbridge/src/topic/getCommandResponseSubscriptionTopic.test.ts b/packages/mqttbridge/src/topic/getCommandResponseSubscriptionTopic.test.ts index ca8763e47..d4431f422 100644 --- a/packages/mqttbridge/src/topic/getCommandResponseSubscriptionTopic.test.ts +++ b/packages/mqttbridge/src/topic/getCommandResponseSubscriptionTopic.test.ts @@ -1,21 +1,21 @@ import { getLoggerMock } from '@purista/core' -import { getDefaultMqttBridgeConfig } from '../getDefaultMqttBridgeConfig.impl.js' import type { MqttBridge } from '../MqttEventBridge.js' +import { getDefaultMqttBridgeConfig } from '../getDefaultMqttBridgeConfig.impl.js' import { getCommandResponseSubscriptionTopic } from './getCommandResponseSubscriptionTopic.impl.js' describe('getCommandResponseSubscriptionTopic', () => { - it('returns the command response topic for the current instance', () => { - const bridge = { - logger: getLoggerMock().mock, - instanceId: 'abc123', - config: { - ...getDefaultMqttBridgeConfig(), - }, - } as any as MqttBridge + it('returns the command response topic for the current instance', () => { + const bridge = { + logger: getLoggerMock().mock, + instanceId: 'abc123', + config: { + ...getDefaultMqttBridgeConfig(), + }, + } as any as MqttBridge - const topic = getCommandResponseSubscriptionTopic.bind(bridge)() + const topic = getCommandResponseSubscriptionTopic.bind(bridge)() - expect(topic).toBe('purista/+/+/+/+/+/+/+/+/abc123/+/+/+') - }) + expect(topic).toBe('purista/+/+/+/+/+/+/+/+/abc123/+/+/+') + }) }) diff --git a/packages/mqttbridge/src/topic/getCommandSubscriptionTopic.impl.ts b/packages/mqttbridge/src/topic/getCommandSubscriptionTopic.impl.ts index 718890a1e..6e622ae65 100644 --- a/packages/mqttbridge/src/topic/getCommandSubscriptionTopic.impl.ts +++ b/packages/mqttbridge/src/topic/getCommandSubscriptionTopic.impl.ts @@ -1,26 +1,26 @@ import { join } from 'node:path/posix' import type { EBMessageAddress } from '@purista/core' -import { convertToSnakeCase, EBMessageType } from '@purista/core' +import { EBMessageType, convertToSnakeCase } from '@purista/core' import type { MqttBridge } from '../MqttEventBridge.js' type GetCommandTopicFn = (this: MqttBridge, address: EBMessageAddress) => string export const getCommandSubscriptionTopic: GetCommandTopicFn = function (address) { - return join( - this.config.topicPrefix, - convertToSnakeCase(EBMessageType.Command), - convertToSnakeCase('+'), - convertToSnakeCase('+'), - convertToSnakeCase('+'), - convertToSnakeCase('+'), - convertToSnakeCase('+'), - convertToSnakeCase('+'), - convertToSnakeCase('+'), - convertToSnakeCase('+'), - convertToSnakeCase(address.serviceName), - convertToSnakeCase(address.serviceVersion), - convertToSnakeCase(address.serviceTarget), - ) + return join( + this.config.topicPrefix, + convertToSnakeCase(EBMessageType.Command), + convertToSnakeCase('+'), + convertToSnakeCase('+'), + convertToSnakeCase('+'), + convertToSnakeCase('+'), + convertToSnakeCase('+'), + convertToSnakeCase('+'), + convertToSnakeCase('+'), + convertToSnakeCase('+'), + convertToSnakeCase(address.serviceName), + convertToSnakeCase(address.serviceVersion), + convertToSnakeCase(address.serviceTarget), + ) } diff --git a/packages/mqttbridge/src/topic/getCommandSubscriptionTopic.test.ts b/packages/mqttbridge/src/topic/getCommandSubscriptionTopic.test.ts index 4920e50f0..7d09e0588 100644 --- a/packages/mqttbridge/src/topic/getCommandSubscriptionTopic.test.ts +++ b/packages/mqttbridge/src/topic/getCommandSubscriptionTopic.test.ts @@ -1,24 +1,24 @@ import { getLoggerMock } from '@purista/core' -import { getDefaultMqttBridgeConfig } from '../getDefaultMqttBridgeConfig.impl.js' import type { MqttBridge } from '../MqttEventBridge.js' +import { getDefaultMqttBridgeConfig } from '../getDefaultMqttBridgeConfig.impl.js' import { getCommandSubscriptionTopic } from './getCommandSubscriptionTopic.impl.js' describe('getCommandSubscriptionTopic', () => { - it('returns the command topic', () => { - const bridge = { - logger: getLoggerMock().mock, - config: { - ...getDefaultMqttBridgeConfig(), - }, - } as any as MqttBridge + it('returns the command topic', () => { + const bridge = { + logger: getLoggerMock().mock, + config: { + ...getDefaultMqttBridgeConfig(), + }, + } as any as MqttBridge - const topic = getCommandSubscriptionTopic.bind(bridge)({ - serviceName: 'testService', - serviceVersion: '1', - serviceTarget: 'testCommand', - }) + const topic = getCommandSubscriptionTopic.bind(bridge)({ + serviceName: 'testService', + serviceVersion: '1', + serviceTarget: 'testCommand', + }) - expect(topic).toBe('purista/command/+/+/+/+/+/+/+/+/test_service/1/test_command') - }) + expect(topic).toBe('purista/command/+/+/+/+/+/+/+/+/test_service/1/test_command') + }) }) diff --git a/packages/mqttbridge/src/topic/getSharedTopicName.impl.ts b/packages/mqttbridge/src/topic/getSharedTopicName.impl.ts index db69d49f7..505117792 100644 --- a/packages/mqttbridge/src/topic/getSharedTopicName.impl.ts +++ b/packages/mqttbridge/src/topic/getSharedTopicName.impl.ts @@ -5,5 +5,5 @@ import type { MqttBridge } from '../MqttEventBridge.js' type GetSharedTopicNameFn = (this: MqttBridge, topic: string) => string export const getSharedTopicName: GetSharedTopicNameFn = function (topic: string) { - return join(this.config.shareTopicPrefix, this.config.shareTopicName, topic) + return join(this.config.shareTopicPrefix, this.config.shareTopicName, topic) } diff --git a/packages/mqttbridge/src/topic/getSharedTopicName.test.ts b/packages/mqttbridge/src/topic/getSharedTopicName.test.ts index 8d3fa1c0b..1f1cca42c 100644 --- a/packages/mqttbridge/src/topic/getSharedTopicName.test.ts +++ b/packages/mqttbridge/src/topic/getSharedTopicName.test.ts @@ -1,21 +1,21 @@ import { getLoggerMock } from '@purista/core' -import { getDefaultMqttBridgeConfig } from '../getDefaultMqttBridgeConfig.impl.js' import type { MqttBridge } from '../MqttEventBridge.js' +import { getDefaultMqttBridgeConfig } from '../getDefaultMqttBridgeConfig.impl.js' import { getSharedTopicName } from './getSharedTopicName.impl.js' describe('getSharedTopicName', () => { - it('returns the topic name for a topic to be a shared one', () => { - const bridge = { - logger: getLoggerMock().mock, - config: { - ...getDefaultMqttBridgeConfig(), - }, - } as any as MqttBridge + it('returns the topic name for a topic to be a shared one', () => { + const bridge = { + logger: getLoggerMock().mock, + config: { + ...getDefaultMqttBridgeConfig(), + }, + } as any as MqttBridge - const originalTopic = 'purista/command/test_service/1/test_command' - const topic = getSharedTopicName.bind(bridge)(originalTopic) + const originalTopic = 'purista/command/test_service/1/test_command' + const topic = getSharedTopicName.bind(bridge)(originalTopic) - expect(topic).toBe('$share/sharedpurista/purista/command/test_service/1/test_command') - }) + expect(topic).toBe('$share/sharedpurista/purista/command/test_service/1/test_command') + }) }) diff --git a/packages/mqttbridge/src/topic/getSubscriptionTopic.impl.ts b/packages/mqttbridge/src/topic/getSubscriptionTopic.impl.ts index b4e63c26c..aee0a2d6b 100644 --- a/packages/mqttbridge/src/topic/getSubscriptionTopic.impl.ts +++ b/packages/mqttbridge/src/topic/getSubscriptionTopic.impl.ts @@ -8,19 +8,19 @@ import type { MqttBridge } from '../MqttEventBridge.js' type GetSubscriptionTopicFn = (this: MqttBridge, subscription: Subscription) => string export const getSubscriptionTopic: GetSubscriptionTopicFn = function (subscription) { - return join( - this.config.topicPrefix, - convertToSnakeCase(subscription.messageType ?? '+'), - convertToSnakeCase(subscription.principalId ?? '+'), - convertToSnakeCase(subscription.tenantId ?? '+'), - convertToSnakeCase(subscription.sender?.instanceId ?? '+'), - convertToSnakeCase(subscription.sender?.serviceName ?? '+'), - convertToSnakeCase(subscription.sender?.serviceVersion ?? '+'), - convertToSnakeCase(subscription.sender?.serviceTarget ?? '+'), - convertToSnakeCase(subscription.eventName ?? '+'), - convertToSnakeCase(subscription.receiver?.instanceId ?? '+'), - convertToSnakeCase(subscription.receiver?.serviceName ?? '+'), - convertToSnakeCase(subscription.receiver?.serviceVersion ?? '+'), - convertToSnakeCase(subscription.receiver?.serviceTarget ?? '+'), - ) + return join( + this.config.topicPrefix, + convertToSnakeCase(subscription.messageType ?? '+'), + convertToSnakeCase(subscription.principalId ?? '+'), + convertToSnakeCase(subscription.tenantId ?? '+'), + convertToSnakeCase(subscription.sender?.instanceId ?? '+'), + convertToSnakeCase(subscription.sender?.serviceName ?? '+'), + convertToSnakeCase(subscription.sender?.serviceVersion ?? '+'), + convertToSnakeCase(subscription.sender?.serviceTarget ?? '+'), + convertToSnakeCase(subscription.eventName ?? '+'), + convertToSnakeCase(subscription.receiver?.instanceId ?? '+'), + convertToSnakeCase(subscription.receiver?.serviceName ?? '+'), + convertToSnakeCase(subscription.receiver?.serviceVersion ?? '+'), + convertToSnakeCase(subscription.receiver?.serviceTarget ?? '+'), + ) } diff --git a/packages/mqttbridge/src/topic/getTopicName.impl.ts b/packages/mqttbridge/src/topic/getTopicName.impl.ts index 4fb357ee3..05d3dfa5e 100644 --- a/packages/mqttbridge/src/topic/getTopicName.impl.ts +++ b/packages/mqttbridge/src/topic/getTopicName.impl.ts @@ -29,21 +29,21 @@ type GetTopicNameFn = (this: MqttBridge, message: EBMessage) => string * */ export const getTopicName: GetTopicNameFn = function (message: EBMessage) { - const empty = this.config.emptyTopicPartString + const empty = this.config.emptyTopicPartString - return join( - this.config.topicPrefix, - convertToSnakeCase(message.messageType), - convertToSnakeCase(message.principalId ?? empty), - convertToSnakeCase(message.tenantId ?? empty), - convertToSnakeCase(message.sender.instanceId), - convertToSnakeCase(message.sender.serviceName), - convertToSnakeCase(message.sender.serviceVersion), - convertToSnakeCase(message.sender.serviceTarget), - convertToSnakeCase(message.eventName ?? empty), - convertToSnakeCase((message as Command).receiver?.instanceId ?? empty), - convertToSnakeCase((message as Command).receiver?.serviceName ?? empty), - convertToSnakeCase((message as Command).receiver?.serviceVersion ?? empty), - convertToSnakeCase((message as Command).receiver?.serviceTarget ?? empty), - ) + return join( + this.config.topicPrefix, + convertToSnakeCase(message.messageType), + convertToSnakeCase(message.principalId ?? empty), + convertToSnakeCase(message.tenantId ?? empty), + convertToSnakeCase(message.sender.instanceId), + convertToSnakeCase(message.sender.serviceName), + convertToSnakeCase(message.sender.serviceVersion), + convertToSnakeCase(message.sender.serviceTarget), + convertToSnakeCase(message.eventName ?? empty), + convertToSnakeCase((message as Command).receiver?.instanceId ?? empty), + convertToSnakeCase((message as Command).receiver?.serviceName ?? empty), + convertToSnakeCase((message as Command).receiver?.serviceVersion ?? empty), + convertToSnakeCase((message as Command).receiver?.serviceTarget ?? empty), + ) } diff --git a/packages/mqttbridge/src/topic/isMatchingTopic.impl.ts b/packages/mqttbridge/src/topic/isMatchingTopic.impl.ts index b53aebfb1..373b42780 100644 --- a/packages/mqttbridge/src/topic/isMatchingTopic.impl.ts +++ b/packages/mqttbridge/src/topic/isMatchingTopic.impl.ts @@ -6,33 +6,33 @@ * @returns */ export const isMatchingTopic = (input: string, pattern: string) => { - const inputArray = input.split('/') - const inLength = inputArray.length - const patternArray = pattern.split('/') - const paLength = patternArray.length + const inputArray = input.split('/') + const inLength = inputArray.length + const patternArray = pattern.split('/') + const paLength = patternArray.length - if (paLength > inLength) { - return false - } + if (paLength > inLength) { + return false + } - if (inLength > paLength && patternArray[paLength - 1] !== '#') { - return false - } + if (inLength > paLength && patternArray[paLength - 1] !== '#') { + return false + } - for (const index in patternArray) { - const part = patternArray[index] - const checkPart = inputArray[index] + for (const index in patternArray) { + const part = patternArray[index] + const checkPart = inputArray[index] - if (part === '#') { - return true - } - if (part === '+') { - continue - } - if (checkPart !== part) { - return false - } - } + if (part === '#') { + return true + } + if (part === '+') { + continue + } + if (checkPart !== part) { + return false + } + } - return true + return true } diff --git a/packages/mqttbridge/src/topic/isMatchingTopic.test.ts b/packages/mqttbridge/src/topic/isMatchingTopic.test.ts index 3e24c8a46..09264b2ba 100644 --- a/packages/mqttbridge/src/topic/isMatchingTopic.test.ts +++ b/packages/mqttbridge/src/topic/isMatchingTopic.test.ts @@ -1,60 +1,60 @@ import { isMatchingTopic } from './isMatchingTopic.impl.js' describe('isMatchingTopic', () => { - it('matches on empty strings', () => { - const input = '' - const pattern = '' - expect(isMatchingTopic(input, pattern)).toBeTruthy() - }) - - it('matches on exact match', () => { - const input = - 'purista/message_type/instance_id/sender_name/sender_version/sender_target/eventname/receiver_name/receiver_version/receiver_target' - const pattern = - 'purista/message_type/instance_id/sender_name/sender_version/sender_target/eventname/receiver_name/receiver_version/receiver_target' - expect(isMatchingTopic(input, pattern)).toBeTruthy() - }) - - it('matches with + placeholder', () => { - const input = - 'purista/message_type/instance_id/sender_name/sender_version/sender_target/eventname/receiver_name/receiver_version/receiver_target' - const pattern = 'purista/message_type/+/+/+/+/+/receiver_name/receiver_version/receiver_target' - expect(isMatchingTopic(input, pattern)).toBeTruthy() - }) - - it('matches with # placeholder', () => { - const input = - 'purista/message_type/instance_id/sender_name/sender_version/sender_target/eventname/receiver_name/receiver_version/receiver_target' - const pattern = 'purista/message_type/#' - expect(isMatchingTopic(input, pattern)).toBeTruthy() - }) - - it('does not match on differences', () => { - const input = - 'purista/message_type/instance_id/sender_name/sender_version/sender_target/eventname/receiver_name/receiver_version/receiver_target' - const pattern = - 'purista/message_type/instance_id/sender_name/sender_version/diff/eventname/receiver_name/receiver_version/receiver_target' - expect(isMatchingTopic(input, pattern)).toBeFalsy() - }) - - it('does not match if the pattern is longer than the input', () => { - const input = 'purista/message_type/instance_id/' - const pattern = - 'purista/message_type/instance_id/sender_name/sender_version/diff/eventname/receiver_name/receiver_version/receiver_target' - expect(isMatchingTopic(input, pattern)).toBeFalsy() - }) - - it('does not match if the pattern is shorter than the input and the pattern is not ending with #', () => { - const input = - 'purista/message_type/instance_id/sender_version/diff/eventname/receiver_name/receiver_version/receiver_target' - const pattern = 'purista/message_type/instance_id/sender_name/' - expect(isMatchingTopic(input, pattern)).toBeFalsy() - }) - - it('matches if the pattern is shorter than the input and the pattern is ending with #', () => { - const input = - 'purista/message_type/instance_id/sender_version/diff/eventname/receiver_name/receiver_version/receiver_target' - const pattern = 'purista/message_type/instance_id/sender_name/#' - expect(isMatchingTopic(input, pattern)).toBeFalsy() - }) + it('matches on empty strings', () => { + const input = '' + const pattern = '' + expect(isMatchingTopic(input, pattern)).toBeTruthy() + }) + + it('matches on exact match', () => { + const input = + 'purista/message_type/instance_id/sender_name/sender_version/sender_target/eventname/receiver_name/receiver_version/receiver_target' + const pattern = + 'purista/message_type/instance_id/sender_name/sender_version/sender_target/eventname/receiver_name/receiver_version/receiver_target' + expect(isMatchingTopic(input, pattern)).toBeTruthy() + }) + + it('matches with + placeholder', () => { + const input = + 'purista/message_type/instance_id/sender_name/sender_version/sender_target/eventname/receiver_name/receiver_version/receiver_target' + const pattern = 'purista/message_type/+/+/+/+/+/receiver_name/receiver_version/receiver_target' + expect(isMatchingTopic(input, pattern)).toBeTruthy() + }) + + it('matches with # placeholder', () => { + const input = + 'purista/message_type/instance_id/sender_name/sender_version/sender_target/eventname/receiver_name/receiver_version/receiver_target' + const pattern = 'purista/message_type/#' + expect(isMatchingTopic(input, pattern)).toBeTruthy() + }) + + it('does not match on differences', () => { + const input = + 'purista/message_type/instance_id/sender_name/sender_version/sender_target/eventname/receiver_name/receiver_version/receiver_target' + const pattern = + 'purista/message_type/instance_id/sender_name/sender_version/diff/eventname/receiver_name/receiver_version/receiver_target' + expect(isMatchingTopic(input, pattern)).toBeFalsy() + }) + + it('does not match if the pattern is longer than the input', () => { + const input = 'purista/message_type/instance_id/' + const pattern = + 'purista/message_type/instance_id/sender_name/sender_version/diff/eventname/receiver_name/receiver_version/receiver_target' + expect(isMatchingTopic(input, pattern)).toBeFalsy() + }) + + it('does not match if the pattern is shorter than the input and the pattern is not ending with #', () => { + const input = + 'purista/message_type/instance_id/sender_version/diff/eventname/receiver_name/receiver_version/receiver_target' + const pattern = 'purista/message_type/instance_id/sender_name/' + expect(isMatchingTopic(input, pattern)).toBeFalsy() + }) + + it('matches if the pattern is shorter than the input and the pattern is ending with #', () => { + const input = + 'purista/message_type/instance_id/sender_version/diff/eventname/receiver_name/receiver_version/receiver_target' + const pattern = 'purista/message_type/instance_id/sender_name/#' + expect(isMatchingTopic(input, pattern)).toBeFalsy() + }) }) diff --git a/packages/mqttbridge/src/types/MqttBridgeConfig.ts b/packages/mqttbridge/src/types/MqttBridgeConfig.ts index 8b417f1c8..4dedc9bef 100644 --- a/packages/mqttbridge/src/types/MqttBridgeConfig.ts +++ b/packages/mqttbridge/src/types/MqttBridgeConfig.ts @@ -6,65 +6,65 @@ import type { QoS } from 'mqtt-packet' * the configuration for the MQTT event bridge */ export type MqttBridgeConfig = Prettify< - { - /** - * the prefix for topic to prevent name collisions - * - * @default purista - */ - topicPrefix: string + { + /** + * the prefix for topic to prevent name collisions + * + * @default purista + */ + topicPrefix: string - /** - * the prefix to be used to dynamically create topic names for shared subscriptions - * - * @default $share - */ - shareTopicPrefix: string + /** + * the prefix to be used to dynamically create topic names for shared subscriptions + * + * @default $share + */ + shareTopicPrefix: string - /** - * the name of the shared topic (similar to pubsub name) - * - * @default sharedpurista - */ - shareTopicName: string + /** + * the name of the shared topic (similar to pubsub name) + * + * @default sharedpurista + */ + shareTopicName: string - /** - * The string which should be used in topics for parts, which are undefined - * - * @default __none__ - */ - emptyTopicPartString: string + /** + * The string which should be used in topics for parts, which are undefined + * + * @default __none__ + */ + emptyTopicPartString: string - /** - * QOS for command, command responses and command response subscriptions messages - * - * @default 1 - */ - qosCommand: QoS + /** + * QOS for command, command responses and command response subscriptions messages + * + * @default 1 + */ + qosCommand: QoS - /** - * QOS for all subscriptions - * - * @default 1 - */ - qoSSubscription: QoS + /** + * QOS for all subscriptions + * + * @default 1 + */ + qoSSubscription: QoS - /** - * - * @default 0 - */ - defaultSessionExpiryInterval: number + /** + * + * @default 0 + */ + defaultSessionExpiryInterval: number - /** - * the message expiry interval in seconds - * - * @default - */ - defaultMessageExpiryInterval: number + /** + * the message expiry interval in seconds + * + * @default + */ + defaultMessageExpiryInterval: number - /** - * allow retry of the initial connect - */ - allowRetries?: boolean - } & IClientOptions + /** + * allow retry of the initial connect + */ + allowRetries?: boolean + } & IClientOptions > diff --git a/packages/mqttbridge/test/integration.test.ts b/packages/mqttbridge/test/integration.test.ts index 9897b8e5a..d841c2f53 100644 --- a/packages/mqttbridge/test/integration.test.ts +++ b/packages/mqttbridge/test/integration.test.ts @@ -14,96 +14,91 @@ const MQTT_PORT = 1883 const EXAMPLE_EVENT = 'exampleEvent' describe('@purista/mqttbridge', () => { - let container: StartedTestContainer - let eventbridge: MqttBridge - const sandbox = createSandbox() - const subscriptionStub = sandbox.stub().resolves() - const logger = getLoggerMock(sandbox) - let service: Service - - beforeAll(async () => { - const source = join(__dirname, 'mosquitto.conf') - - container = await new GenericContainer('eclipse-mosquitto') - .withExposedPorts({ - container: MQTT_PORT, - host: MQTT_PORT, - }) - .withBindMounts([ - { - source, - target: '/mosquitto/config/mosquitto.conf', - }, - ]) - .withLogConsumer((_stream) => { - // stream.on('data', (line) => console.debug(line)) - // eslint-disable-next-line no-console - // stream.on('err', (line) => console.error(line)) - }) - .start() - - eventbridge = new MqttBridge({ logger: logger.mock }) - await eventbridge.start() - - const subscriptionBuilder = theServiceV1Service - .getSubscriptionBuilder('sendWelcomeEmail', 'send a welcome mail to new registered users') - .subscribeToEvent(EXAMPLE_EVENT) - .addPayloadSchema(z.any()) - .setSubscriptionFunction(subscriptionStub) - - theServiceServiceBuilder.addSubscriptionDefinition(subscriptionBuilder.getDefinition()) - - service = await theServiceServiceBuilder.getInstance(eventbridge, { logger: getLoggerMock(sandbox).mock }) - await service.start() - }) - - afterAll(async () => { - await service.destroy() - await eventbridge.destroy() - await container.stop() - }) - - afterEach(() => { - sandbox.resetHistory() - }) - - it('can invoke ping command', async () => { - const command = getCommandMessageMock({ - receiver: { - serviceName: service.info.serviceName, - serviceVersion: service.info.serviceVersion, - serviceTarget: 'ping', - }, - sender: { - serviceName: service.info.serviceName, - serviceVersion: service.info.serviceVersion, - serviceTarget: 'some', - instanceId: eventbridge.instanceId, - }, - payload: { - payload: undefined, - parameter: { - required: 'yes', - }, - }, - }) - const result = await eventbridge.invoke(command) - - expect(result).toEqual({ - ping: true, - }) - - expect(true).toBeTruthy() - }) - - it('receives subscriptions', async () => { - const payload = { example: 'payload' } - const commandResponse = getCommandSuccessMessageMock(payload, { eventName: EXAMPLE_EVENT }) - - await eventbridge.emitMessage(commandResponse) - - await new Promise((resolve) => setTimeout(resolve, 3000)) - - expect(subscriptionStub.called).toBeTruthy() - }) + let container: StartedTestContainer + let eventbridge: MqttBridge + const sandbox = createSandbox() + const subscriptionStub = sandbox.stub().resolves() + const logger = getLoggerMock(sandbox) + let service: Service + + beforeAll(async () => { + const source = join(__dirname, 'mosquitto.conf') + + container = await new GenericContainer('eclipse-mosquitto') + .withExposedPorts({ + container: MQTT_PORT, + host: MQTT_PORT, + }) + .withBindMounts([ + { + source, + target: '/mosquitto/config/mosquitto.conf', + }, + ]) + .start() + + eventbridge = new MqttBridge({ logger: logger.mock }) + await eventbridge.start() + + const subscriptionBuilder = theServiceV1Service + .getSubscriptionBuilder('sendWelcomeEmail', 'send a welcome mail to new registered users') + .subscribeToEvent(EXAMPLE_EVENT) + .addPayloadSchema(z.any()) + .setSubscriptionFunction(subscriptionStub) + + theServiceServiceBuilder.addSubscriptionDefinition(subscriptionBuilder.getDefinition()) + + service = await theServiceServiceBuilder.getInstance(eventbridge, { logger: getLoggerMock(sandbox).mock }) + await service.start() + }) + + afterAll(async () => { + await service.destroy() + await eventbridge.destroy() + await container.stop() + }) + + afterEach(() => { + sandbox.resetHistory() + }) + + it('can invoke ping command', async () => { + const command = getCommandMessageMock({ + receiver: { + serviceName: service.info.serviceName, + serviceVersion: service.info.serviceVersion, + serviceTarget: 'ping', + }, + sender: { + serviceName: service.info.serviceName, + serviceVersion: service.info.serviceVersion, + serviceTarget: 'some', + instanceId: eventbridge.instanceId, + }, + payload: { + payload: undefined, + parameter: { + required: 'yes', + }, + }, + }) + const result = await eventbridge.invoke(command) + + expect(result).toEqual({ + ping: true, + }) + + expect(true).toBeTruthy() + }) + + it('receives subscriptions', async () => { + const payload = { example: 'payload' } + const commandResponse = getCommandSuccessMessageMock(payload, { eventName: EXAMPLE_EVENT }) + + await eventbridge.emitMessage(commandResponse) + + await new Promise(resolve => setTimeout(resolve, 3000)) + + expect(subscriptionStub.called).toBeTruthy() + }) }) diff --git a/packages/mqttbridge/tsconfig.json b/packages/mqttbridge/tsconfig.json index cdb332b03..7b117ebaf 100644 --- a/packages/mqttbridge/tsconfig.json +++ b/packages/mqttbridge/tsconfig.json @@ -1,20 +1,12 @@ { - "extends": "../../tsconfig.json", - "compilerOptions": { - "outDir": "./dist", - "declaration": true, - "sourceMap": false, - "declarationMap": true, - "types": [ - "vitest/globals", - "node" - ] - }, - "include": [ - "./src/**/*", - "./test/*", - ], - "exclude": [ - "./**/*.d.ts" - ] -} \ No newline at end of file + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "./dist", + "declaration": true, + "sourceMap": false, + "declarationMap": true, + "types": ["vitest/globals", "node"] + }, + "include": ["./src/**/*", "./test/*"], + "exclude": ["./**/*.d.ts"] +} diff --git a/packages/mqttbridge/typedoc.json b/packages/mqttbridge/typedoc.json index 355bf0f98..71c4b2283 100644 --- a/packages/mqttbridge/typedoc.json +++ b/packages/mqttbridge/typedoc.json @@ -1,6 +1,5 @@ { - - "extends": ["../../typedoc.base.json"], - "entryPoints": ["src/index.ts"], - "tsconfig": "./tsconfig.json" -} \ No newline at end of file + "extends": ["../../typedoc.base.json"], + "entryPoints": ["src/index.ts"], + "tsconfig": "./tsconfig.json" +} diff --git a/packages/nats-config-store/jsr.json b/packages/nats-config-store/jsr.json new file mode 100644 index 000000000..7c414bda7 --- /dev/null +++ b/packages/nats-config-store/jsr.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://jsr.io/schema/config-file.v1.json", + "name": "@purista/nats-config-store", + "version": "1.11.0", + "description": "State store with NATS as backend", + "keywords": ["purista", "nats", "typescript", "javascript"], + "exports": "./dist/esm/index.js", + "publish": { + "include": ["dist/**/*.js", "dist/**/*.d.ts", "README.md", "package.json"], + "exclude": [ + "src", + ".github", + ".vscode", + ".zed", + "!dist", + "!dist/**/*.js", + "!dist/**/*.d.ts", + ".tshy", + ".tshy-build", + "vendor", + "docs", + "typedoc.json", + "..eslintcache", + ".npmignore" + ] + } +} diff --git a/packages/nats-config-store/package.json b/packages/nats-config-store/package.json index e606619c6..61969e051 100644 --- a/packages/nats-config-store/package.json +++ b/packages/nats-config-store/package.json @@ -1,64 +1,61 @@ { - "name": "@purista/nats-config-store", - "version": "1.11.0", - "description": "State store with NATS as backend", - "homepage": "https://purista.dev", - "repository": { - "type": "git", - "url": "git@github.com:sebastianwessel/purista.git" - }, - "author": "Sebastian Wessel", - "license": "ISC", - "type": "module", - "main": "./dist/commonjs/index.js", - "exports": { - "./package.json": "./package.json", - ".": { - "import": { - "types": "./dist/esm/index.d.ts", - "default": "./dist/esm/index.js" - }, - "require": { - "types": "./dist/commonjs/index.d.ts", - "default": "./dist/commonjs/index.js" - } - } - }, - "files": [ - "dist/**/*" - ], - "publishConfig": { - "access": "public" - }, - "engines": { - "node": ">=18.15" - }, - "scripts": { - "lint": "eslint . --ext .ts,.json --cache . --fix", - "test": "vitest", - "build": "rimraf dist && tshy" - }, - "tshy": { - "exclude": [ - "src/**/*.test.ts" - ], - "exports": { - "./package.json": "./package.json", - ".": "./src/index.ts" - } - }, - "devDependencies": { - "@testcontainers/nats": "^10.6.0", - "@types/node": "^20.11.17", - "@types/sinon": "^17.0.3", - "sinon": "^17.0.1", - "tshy": "^1.11.1", - "vitest": "^1.3.0" - }, - "dependencies": { - "@purista/core": "*", - "nats": "^2.19.0" - }, - "peerDependenciesMeta": {}, - "types": "./dist/commonjs/index.d.ts" + "name": "@purista/nats-config-store", + "version": "1.11.0", + "description": "State store with NATS as backend", + "homepage": "https://purista.dev", + "repository": { + "type": "git", + "url": "git@github.com:puristajs/purista.git" + }, + "author": "Sebastian Wessel", + "license": "ISC", + "type": "module", + "main": "./dist/commonjs/index.js", + "exports": { + "./package.json": "./package.json", + ".": { + "import": { + "types": "./dist/esm/index.d.ts", + "default": "./dist/esm/index.js" + }, + "require": { + "types": "./dist/commonjs/index.d.ts", + "default": "./dist/commonjs/index.js" + } + } + }, + "files": ["dist/**/*"], + "publishConfig": { + "access": "public" + }, + "engines": { + "node": ">=18.15" + }, + "scripts": { + "lint": "npx @biomejs/biome check --write", + "test": "vitest", + "build": "rimraf dist && tshy" + }, + "tshy": { + "exclude": ["src/**/*.test.ts"], + "exports": { + "./package.json": "./package.json", + ".": "./src/index.ts" + } + }, + "devDependencies": { + "@testcontainers/nats": "^10.12.0", + "@types/node": "^22.5.1", + "@types/sinon": "^17.0.3", + "sinon": "^19.0.2", + "tshy": "^3.0.2", + "vitest": "^3.0.4" + }, + "dependencies": { + "@purista/core": "*", + "nats": "^2.28.1" + }, + "peerDependenciesMeta": {}, + "types": "./dist/commonjs/index.d.ts", + "module": "./dist/esm/index.js" } diff --git a/packages/nats-config-store/src/NatsConfigStore.impl.ts b/packages/nats-config-store/src/NatsConfigStore.impl.ts index 9017807f7..173cbde02 100644 --- a/packages/nats-config-store/src/NatsConfigStore.impl.ts +++ b/packages/nats-config-store/src/NatsConfigStore.impl.ts @@ -1,7 +1,7 @@ import type { ObjectWithKeysFromStringArray, StoreBaseConfig } from '@purista/core' import { ConfigStoreBaseClass, StatusCode, UnhandledError } from '@purista/core' import type { KV, NatsConnection } from 'nats' -import { connect, JSONCodec } from 'nats' +import { JSONCodec, connect } from 'nats' import type { NatsConfigStoreConfig } from './types/index.js' @@ -29,92 +29,92 @@ console.log(value) // outputs: undefined ``` */ export class NatsConfigStore extends ConfigStoreBaseClass { - public connection: NatsConnection | undefined - - sc = JSONCodec() - kv: KV | undefined - - constructor(config?: StoreBaseConfig>) { - const conf = { - keyValueStoreName: 'purista-config-store', - ...config, - } - super('NatsConfigStore', { ...conf }) - } - - async getStore() { - if (this.kv) { - return this.kv - } - - try { - this.connection = await connect({ ...this.config, name: this.name }) - } catch (error) { - this.connection = undefined - const err = UnhandledError.fromError(error) - this.logger.error({ err }, err.message) - throw err - } - - if (!this.connection.info?.jetstream) { - const err = new UnhandledError(StatusCode.BadGateway, 'JetStream is not enabled on NATS server') - this.logger.error({ err }, err.message) - await this.connection.close() - this.connection = undefined - throw err - } - - const js = this.connection.jetstream() - this.kv = await js.views.kv(this.config.keyValueStoreName, this.config) - - return this.kv - } - - protected async getConfigImpl( - ...stateNames: ConfigNames - ): Promise> { - const store = await this.getStore() - - const result: Record = {} - for await (const name of stateNames) { - try { - const entry = await store.get(name) - result[name] = entry?.value && entry?.value.length > 0 ? this.sc.decode(entry.value) : undefined - } catch (err) { - const msg = `error in state store getting value ${name}` - this.logger.error({ err }, msg) - throw new UnhandledError(StatusCode.InternalServerError, msg) - } - } - return result as ObjectWithKeysFromStringArray - } - - protected async removeConfigImpl(stateName: string) { - const store = await this.getStore() - - try { - await store.delete(stateName) - } catch (err) { - const msg = `error in state store removing value ${stateName}` - this.logger.error({ err }, msg) - throw new UnhandledError(StatusCode.InternalServerError, msg) - } - } - - protected async setConfigImpl(stateName: string, stateValue: unknown) { - const store = await this.getStore() - - try { - await store.put(stateName, this.sc.encode(stateValue)) - } catch (err) { - const msg = `error in state store setting value ${stateName}` - this.logger.error({ err }, msg) - throw new UnhandledError(StatusCode.InternalServerError, msg) - } - } - - async destroy() { - await this.connection?.drain() - await this.connection?.close() - } + public connection: NatsConnection | undefined + + sc = JSONCodec() + kv: KV | undefined + + constructor(config?: StoreBaseConfig>) { + const conf = { + keyValueStoreName: 'purista-config-store', + ...config, + } + super('NatsConfigStore', { ...conf }) + } + + async getStore() { + if (this.kv) { + return this.kv + } + + try { + this.connection = await connect({ ...this.config, name: this.name }) + } catch (error) { + this.connection = undefined + const err = UnhandledError.fromError(error) + this.logger.error({ err }, err.message) + throw err + } + + if (!this.connection.info?.jetstream) { + const err = new UnhandledError(StatusCode.BadGateway, 'JetStream is not enabled on NATS server') + this.logger.error({ err }, err.message) + await this.connection.close() + this.connection = undefined + throw err + } + + const js = this.connection.jetstream() + this.kv = await js.views.kv(this.config.keyValueStoreName, this.config) + + return this.kv + } + + protected async getConfigImpl( + ...stateNames: ConfigNames + ): Promise> { + const store = await this.getStore() + + const result: Record = {} + for await (const name of stateNames) { + try { + const entry = await store.get(name) + result[name] = entry?.value && entry?.value.length > 0 ? this.sc.decode(entry.value) : undefined + } catch (err) { + const msg = `error in state store getting value ${name}` + this.logger.error({ err }, msg) + throw new UnhandledError(StatusCode.InternalServerError, msg) + } + } + return result as ObjectWithKeysFromStringArray + } + + protected async removeConfigImpl(stateName: string) { + const store = await this.getStore() + + try { + await store.delete(stateName) + } catch (err) { + const msg = `error in state store removing value ${stateName}` + this.logger.error({ err }, msg) + throw new UnhandledError(StatusCode.InternalServerError, msg) + } + } + + protected async setConfigImpl(stateName: string, stateValue: unknown) { + const store = await this.getStore() + + try { + await store.put(stateName, this.sc.encode(stateValue)) + } catch (err) { + const msg = `error in state store setting value ${stateName}` + this.logger.error({ err }, msg) + throw new UnhandledError(StatusCode.InternalServerError, msg) + } + } + + async destroy() { + await this.connection?.drain() + await this.connection?.close() + } } diff --git a/packages/nats-config-store/src/index.test.ts b/packages/nats-config-store/src/index.test.ts index b8bc6acc9..2f161e1f5 100644 --- a/packages/nats-config-store/src/index.test.ts +++ b/packages/nats-config-store/src/index.test.ts @@ -1,11 +1,11 @@ import { NatsConfigStore, puristaVersion } from './index.js' describe('exports redis-state-store', () => { - it('has a version', () => { - expect(puristaVersion).toBeDefined() - }) + it('has a version', () => { + expect(puristaVersion).toBeDefined() + }) - it('exports NatsConfigStore', () => { - expect(NatsConfigStore).toBeDefined() - }) + it('exports NatsConfigStore', () => { + expect(NatsConfigStore).toBeDefined() + }) }) diff --git a/packages/nats-config-store/src/types/NatsConfigStoreConfig.ts b/packages/nats-config-store/src/types/NatsConfigStoreConfig.ts index ce5c9e14a..a81d9e32c 100644 --- a/packages/nats-config-store/src/types/NatsConfigStoreConfig.ts +++ b/packages/nats-config-store/src/types/NatsConfigStoreConfig.ts @@ -2,8 +2,8 @@ import type { Prettify } from '@purista/core' import type { ConnectionOptions, KvOptions } from 'nats' export type NatsConfigStoreConfig = Prettify< - { - keyValueStoreName: string - } & ConnectionOptions & - Partial + { + keyValueStoreName: string + } & ConnectionOptions & + Partial > diff --git a/packages/nats-config-store/test/integration.test.ts b/packages/nats-config-store/test/integration.test.ts index 7914923a9..1f2cda352 100644 --- a/packages/nats-config-store/test/integration.test.ts +++ b/packages/nats-config-store/test/integration.test.ts @@ -5,61 +5,54 @@ import { NatsContainer } from '@testcontainers/nats' import { NatsConfigStore } from '../src/NatsConfigStore.impl.js' describe('@purista/nats-config-store', () => { - let container: StartedNatsContainer - - beforeAll(async () => { - container = await new NatsContainer('nats:alpine') - .withArg('-js', '-js') - .withLogConsumer((_stream) => { - // stream.on('data', (line) => console.debug(line)) - // eslint-disable-next-line no-console - // stream.on('err', (line) => console.error(line)) - }) - .start() - }) - - afterAll(async () => { - await container.stop() - }) - - it('set, get and remove values', async () => { - const store = new NatsConfigStore({ - ...container.getConnectionOptions(), - enableRemove: true, - enableSet: true, - logger: getLoggerMock().mock, - }) - - await expect(store.setConfig('myConfig', { some: 'value' })).resolves.toBeUndefined() - - const value = await store.getConfig('myConfig') - expect(value).toStrictEqual({ - myConfig: { some: 'value' }, - }) - - await expect(store.removeConfig('myConfig')).resolves.toBeUndefined() - await expect(store.getConfig('myConfig')).resolves.toStrictEqual({ - myConfig: undefined, - }) - - await expect(store.destroy()).resolves.toBeUndefined() - }) - - it('throws on disabled features', async () => { - const store = new NatsConfigStore({ - enableGet: false, - enableRemove: false, - enableSet: false, - ...container.getConnectionOptions(), - logger: getLoggerMock().mock, - }) - - await expect(store.setConfig('myConfig', { some: 'value' })).rejects.toThrow( - 'set config at store is disabled by config', - ) - await expect(store.getConfig('myConfig')).rejects.toThrow('get config from store is disabled by config') - await expect(store.removeConfig('myConfig')).rejects.toThrow('remove config from store is disabled by config') - - await expect(store.destroy()).resolves.toBeUndefined() - }) + let container: StartedNatsContainer + + beforeAll(async () => { + container = await new NatsContainer('nats:alpine').withArg('-js', '-js').start() + }) + + afterAll(async () => { + await container.stop() + }) + + it('set, get and remove values', async () => { + const store = new NatsConfigStore({ + ...container.getConnectionOptions(), + enableRemove: true, + enableSet: true, + logger: getLoggerMock().mock, + }) + + await expect(store.setConfig('myConfig', { some: 'value' })).resolves.toBeUndefined() + + const value = await store.getConfig('myConfig') + expect(value).toStrictEqual({ + myConfig: { some: 'value' }, + }) + + await expect(store.removeConfig('myConfig')).resolves.toBeUndefined() + await expect(store.getConfig('myConfig')).resolves.toStrictEqual({ + myConfig: undefined, + }) + + await expect(store.destroy()).resolves.toBeUndefined() + }) + + it('throws on disabled features', async () => { + const store = new NatsConfigStore({ + enableGet: false, + enableRemove: false, + enableSet: false, + ...container.getConnectionOptions(), + logger: getLoggerMock().mock, + }) + + await expect(store.setConfig('myConfig', { some: 'value' })).rejects.toThrow( + 'set config at store is disabled by config', + ) + await expect(store.getConfig('myConfig')).rejects.toThrow('get config from store is disabled by config') + await expect(store.removeConfig('myConfig')).rejects.toThrow('remove config from store is disabled by config') + + await expect(store.destroy()).resolves.toBeUndefined() + }) }) diff --git a/packages/nats-config-store/tsconfig.json b/packages/nats-config-store/tsconfig.json index 42dac6999..a2f689e2f 100644 --- a/packages/nats-config-store/tsconfig.json +++ b/packages/nats-config-store/tsconfig.json @@ -1,21 +1,13 @@ { - "extends": "../../tsconfig.json", - "compilerOptions": { - "outDir": "./dist", - "declaration": true, - "sourceMap": false, - "declarationMap": true, - "types": [ - "vitest/globals", - "node" - ] - }, - "exclude": [ - "./**/*.d.ts" - ], - - "include": [ - "./src/**/*", - "./test/*", - ], -} \ No newline at end of file + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "./dist", + "declaration": true, + "sourceMap": false, + "declarationMap": true, + "types": ["vitest/globals", "node"] + }, + "exclude": ["./**/*.d.ts"], + + "include": ["./src/**/*", "./test/*"] +} diff --git a/packages/nats-config-store/typedoc.json b/packages/nats-config-store/typedoc.json index 355bf0f98..71c4b2283 100644 --- a/packages/nats-config-store/typedoc.json +++ b/packages/nats-config-store/typedoc.json @@ -1,6 +1,5 @@ { - - "extends": ["../../typedoc.base.json"], - "entryPoints": ["src/index.ts"], - "tsconfig": "./tsconfig.json" -} \ No newline at end of file + "extends": ["../../typedoc.base.json"], + "entryPoints": ["src/index.ts"], + "tsconfig": "./tsconfig.json" +} diff --git a/packages/nats-state-store/jsr.json b/packages/nats-state-store/jsr.json new file mode 100644 index 000000000..f9ef52778 --- /dev/null +++ b/packages/nats-state-store/jsr.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://jsr.io/schema/config-file.v1.json", + "name": "@purista/nats-state-store", + "version": "1.11.0", + "description": "State store with NATS as backend", + "keywords": ["purista", "nats", "typescript", "javascript"], + "exports": "./dist/esm/index.js", + "publish": { + "include": ["dist/**/*.js", "dist/**/*.d.ts", "README.md", "package.json"], + "exclude": [ + "src", + ".github", + ".vscode", + ".zed", + "!dist", + "!dist/**/*.js", + "!dist/**/*.d.ts", + ".tshy", + ".tshy-build", + "vendor", + "docs", + "typedoc.json", + "..eslintcache", + ".npmignore" + ] + } +} diff --git a/packages/nats-state-store/package.json b/packages/nats-state-store/package.json index 582389642..d32347b0b 100644 --- a/packages/nats-state-store/package.json +++ b/packages/nats-state-store/package.json @@ -1,64 +1,61 @@ { - "name": "@purista/nats-state-store", - "version": "1.11.0", - "description": "State store with NATS as backend", - "homepage": "https://purista.dev", - "repository": { - "type": "git", - "url": "git@github.com:sebastianwessel/purista.git" - }, - "author": "Sebastian Wessel", - "license": "ISC", - "type": "module", - "main": "./dist/commonjs/index.js", - "exports": { - "./package.json": "./package.json", - ".": { - "import": { - "types": "./dist/esm/index.d.ts", - "default": "./dist/esm/index.js" - }, - "require": { - "types": "./dist/commonjs/index.d.ts", - "default": "./dist/commonjs/index.js" - } - } - }, - "files": [ - "dist/**/*" - ], - "publishConfig": { - "access": "public" - }, - "engines": { - "node": ">=18.15" - }, - "scripts": { - "lint": "eslint . --ext .ts,.json --cache . --fix", - "test": "vitest", - "build": "rimraf dist && tshy" - }, - "tshy": { - "exclude": [ - "src/**/*.test.ts" - ], - "exports": { - "./package.json": "./package.json", - ".": "./src/index.ts" - } - }, - "devDependencies": { - "@testcontainers/nats": "^10.6.0", - "@types/node": "^20.11.17", - "@types/sinon": "^17.0.3", - "sinon": "^17.0.1", - "tshy": "^1.11.1", - "vitest": "^1.3.0" - }, - "dependencies": { - "@purista/core": "*", - "nats": "^2.19.0" - }, - "peerDependenciesMeta": {}, - "types": "./dist/commonjs/index.d.ts" + "name": "@purista/nats-state-store", + "version": "1.11.0", + "description": "State store with NATS as backend", + "homepage": "https://purista.dev", + "repository": { + "type": "git", + "url": "git@github.com:puristajs/purista.git" + }, + "author": "Sebastian Wessel", + "license": "ISC", + "type": "module", + "main": "./dist/commonjs/index.js", + "exports": { + "./package.json": "./package.json", + ".": { + "import": { + "types": "./dist/esm/index.d.ts", + "default": "./dist/esm/index.js" + }, + "require": { + "types": "./dist/commonjs/index.d.ts", + "default": "./dist/commonjs/index.js" + } + } + }, + "files": ["dist/**/*"], + "publishConfig": { + "access": "public" + }, + "engines": { + "node": ">=18.15" + }, + "scripts": { + "lint": "npx @biomejs/biome check --write", + "test": "vitest", + "build": "rimraf dist && tshy" + }, + "tshy": { + "exclude": ["src/**/*.test.ts"], + "exports": { + "./package.json": "./package.json", + ".": "./src/index.ts" + } + }, + "devDependencies": { + "@testcontainers/nats": "^10.12.0", + "@types/node": "^22.5.1", + "@types/sinon": "^17.0.3", + "sinon": "^19.0.2", + "tshy": "^3.0.2", + "vitest": "^3.0.4" + }, + "dependencies": { + "@purista/core": "*", + "nats": "^2.28.1" + }, + "peerDependenciesMeta": {}, + "types": "./dist/commonjs/index.d.ts", + "module": "./dist/esm/index.js" } diff --git a/packages/nats-state-store/src/NatsStateStore.impl.ts b/packages/nats-state-store/src/NatsStateStore.impl.ts index aa1d6149b..7836390b1 100644 --- a/packages/nats-state-store/src/NatsStateStore.impl.ts +++ b/packages/nats-state-store/src/NatsStateStore.impl.ts @@ -1,7 +1,7 @@ import type { ObjectWithKeysFromStringArray, StoreBaseConfig } from '@purista/core' import { StateStoreBaseClass, StatusCode, UnhandledError } from '@purista/core' import type { KV, NatsConnection } from 'nats' -import { connect, JSONCodec } from 'nats' +import { JSONCodec, connect } from 'nats' import type { NatsStateStoreConfig } from './types/index.js' @@ -29,92 +29,92 @@ console.log(value) // outputs: undefined ``` */ export class NatsStateStore extends StateStoreBaseClass { - public connection: NatsConnection | undefined - - sc = JSONCodec() - kv: KV | undefined - - constructor(config?: StoreBaseConfig>) { - const conf = { - keyValueStoreName: 'purista-state-store', - ...config, - } - super('NatsStateStore', { ...conf }) - } - - async getStore() { - if (this.kv) { - return this.kv - } - - try { - this.connection = await connect({ ...this.config, name: this.name }) - } catch (error) { - this.connection = undefined - const err = UnhandledError.fromError(error) - this.logger.error({ err }, err.message) - throw err - } - - if (!this.connection.info?.jetstream) { - const err = new UnhandledError(StatusCode.BadGateway, 'JetStream is not enabled on NATS server') - this.logger.error({ err }, err.message) - await this.connection.close() - this.connection = undefined - throw err - } - - const js = this.connection.jetstream() - this.kv = await js.views.kv(this.config.keyValueStoreName, this.config) - - return this.kv - } - - protected async getStateImpl( - ...stateNames: StateNames - ): Promise> { - const store = await this.getStore() - - const result: Record = {} - for await (const name of stateNames) { - try { - const entry = await store.get(name) - result[name] = entry?.value && entry?.value.length > 0 ? this.sc.decode(entry.value) : undefined - } catch (err) { - const msg = `error in state store getting value ${name}` - this.logger.error({ err }, msg) - throw new UnhandledError(StatusCode.InternalServerError, msg) - } - } - return result as ObjectWithKeysFromStringArray - } - - protected async removeStateImpl(stateName: string) { - const store = await this.getStore() - - try { - await store.delete(stateName) - } catch (err) { - const msg = `error in state store removing value ${stateName}` - this.logger.error({ err }, msg) - throw new UnhandledError(StatusCode.InternalServerError, msg) - } - } - - protected async setStateImpl(stateName: string, stateValue: unknown) { - const store = await this.getStore() - - try { - await store.put(stateName, this.sc.encode(stateValue)) - } catch (err) { - const msg = `error in state store setting value ${stateName}` - this.logger.error({ err }, msg) - throw new UnhandledError(StatusCode.InternalServerError, msg) - } - } - - async destroy() { - await this.connection?.drain() - await this.connection?.close() - } + public connection: NatsConnection | undefined + + sc = JSONCodec() + kv: KV | undefined + + constructor(config?: StoreBaseConfig>) { + const conf = { + keyValueStoreName: 'purista-state-store', + ...config, + } + super('NatsStateStore', { ...conf }) + } + + async getStore() { + if (this.kv) { + return this.kv + } + + try { + this.connection = await connect({ ...this.config, name: this.name }) + } catch (error) { + this.connection = undefined + const err = UnhandledError.fromError(error) + this.logger.error({ err }, err.message) + throw err + } + + if (!this.connection.info?.jetstream) { + const err = new UnhandledError(StatusCode.BadGateway, 'JetStream is not enabled on NATS server') + this.logger.error({ err }, err.message) + await this.connection.close() + this.connection = undefined + throw err + } + + const js = this.connection.jetstream() + this.kv = await js.views.kv(this.config.keyValueStoreName, this.config) + + return this.kv + } + + protected async getStateImpl( + ...stateNames: StateNames + ): Promise> { + const store = await this.getStore() + + const result: Record = {} + for await (const name of stateNames) { + try { + const entry = await store.get(name) + result[name] = entry?.value && entry?.value.length > 0 ? this.sc.decode(entry.value) : undefined + } catch (err) { + const msg = `error in state store getting value ${name}` + this.logger.error({ err }, msg) + throw new UnhandledError(StatusCode.InternalServerError, msg) + } + } + return result as ObjectWithKeysFromStringArray + } + + protected async removeStateImpl(stateName: string) { + const store = await this.getStore() + + try { + await store.delete(stateName) + } catch (err) { + const msg = `error in state store removing value ${stateName}` + this.logger.error({ err }, msg) + throw new UnhandledError(StatusCode.InternalServerError, msg) + } + } + + protected async setStateImpl(stateName: string, stateValue: unknown) { + const store = await this.getStore() + + try { + await store.put(stateName, this.sc.encode(stateValue)) + } catch (err) { + const msg = `error in state store setting value ${stateName}` + this.logger.error({ err }, msg) + throw new UnhandledError(StatusCode.InternalServerError, msg) + } + } + + async destroy() { + await this.connection?.drain() + await this.connection?.close() + } } diff --git a/packages/nats-state-store/src/index.test.ts b/packages/nats-state-store/src/index.test.ts index 0a5297099..8dc7b9432 100644 --- a/packages/nats-state-store/src/index.test.ts +++ b/packages/nats-state-store/src/index.test.ts @@ -1,11 +1,11 @@ import { NatsStateStore, puristaVersion } from './index.js' describe('exports redis-state-store', () => { - it('has a version', () => { - expect(puristaVersion).toBeDefined() - }) + it('has a version', () => { + expect(puristaVersion).toBeDefined() + }) - it('exports NatsStateStore', () => { - expect(NatsStateStore).toBeDefined() - }) + it('exports NatsStateStore', () => { + expect(NatsStateStore).toBeDefined() + }) }) diff --git a/packages/nats-state-store/src/types/NatsStateStoreConfig.ts b/packages/nats-state-store/src/types/NatsStateStoreConfig.ts index ec0ade38b..b559b6e47 100644 --- a/packages/nats-state-store/src/types/NatsStateStoreConfig.ts +++ b/packages/nats-state-store/src/types/NatsStateStoreConfig.ts @@ -2,8 +2,8 @@ import type { Prettify } from '@purista/core' import type { ConnectionOptions, KvOptions } from 'nats' export type NatsStateStoreConfig = Prettify< - { - keyValueStoreName: string - } & ConnectionOptions & - Partial + { + keyValueStoreName: string + } & ConnectionOptions & + Partial > diff --git a/packages/nats-state-store/test/integration.test.ts b/packages/nats-state-store/test/integration.test.ts index bdbbbcada..6c5d6537b 100644 --- a/packages/nats-state-store/test/integration.test.ts +++ b/packages/nats-state-store/test/integration.test.ts @@ -5,56 +5,49 @@ import { NatsContainer } from '@testcontainers/nats' import { NatsStateStore } from '../src/NatsStateStore.impl.js' describe('@purista/nats-state-store', () => { - let container: StartedNatsContainer - - beforeAll(async () => { - container = await new NatsContainer('nats:alpine') - .withArg('-js', '-js') - .withLogConsumer((_stream) => { - // stream.on('data', (line) => console.debug(line)) - // eslint-disable-next-line no-console - // stream.on('err', (line) => console.error(line)) - }) - .start() - }) - - afterAll(async () => { - await container.stop() - }) - - it('set, get and remove values', async () => { - const store = new NatsStateStore({ ...container.getConnectionOptions(), logger: getLoggerMock().mock }) - - await expect(store.setState('myState', { some: 'value' })).resolves.toBeUndefined() - - const value = await store.getState('myState') - expect(value).toStrictEqual({ - myState: { some: 'value' }, - }) - - await expect(store.removeState('myState')).resolves.toBeUndefined() - await expect(store.getState('myState')).resolves.toStrictEqual({ - myState: undefined, - }) - - await expect(store.destroy()).resolves.toBeUndefined() - }) - - it('throws on disabled features', async () => { - const store = new NatsStateStore({ - enableGet: false, - enableRemove: false, - enableSet: false, - ...container.getConnectionOptions(), - logger: getLoggerMock().mock, - }) - - await expect(store.setState('myState', { some: 'value' })).rejects.toThrow( - 'set state at store is disabled by config', - ) - await expect(store.getState('myState')).rejects.toThrow('get state from store is disabled by config') - await expect(store.removeState('myState')).rejects.toThrow('remove state from store is disabled by config') - - await expect(store.destroy()).resolves.toBeUndefined() - }) + let container: StartedNatsContainer + + beforeAll(async () => { + container = await new NatsContainer('nats:alpine').withArg('-js', '-js').start() + }) + + afterAll(async () => { + await container.stop() + }) + + it('set, get and remove values', async () => { + const store = new NatsStateStore({ ...container.getConnectionOptions(), logger: getLoggerMock().mock }) + + await expect(store.setState('myState', { some: 'value' })).resolves.toBeUndefined() + + const value = await store.getState('myState') + expect(value).toStrictEqual({ + myState: { some: 'value' }, + }) + + await expect(store.removeState('myState')).resolves.toBeUndefined() + await expect(store.getState('myState')).resolves.toStrictEqual({ + myState: undefined, + }) + + await expect(store.destroy()).resolves.toBeUndefined() + }) + + it('throws on disabled features', async () => { + const store = new NatsStateStore({ + enableGet: false, + enableRemove: false, + enableSet: false, + ...container.getConnectionOptions(), + logger: getLoggerMock().mock, + }) + + await expect(store.setState('myState', { some: 'value' })).rejects.toThrow( + 'set state at store is disabled by config', + ) + await expect(store.getState('myState')).rejects.toThrow('get state from store is disabled by config') + await expect(store.removeState('myState')).rejects.toThrow('remove state from store is disabled by config') + + await expect(store.destroy()).resolves.toBeUndefined() + }) }) diff --git a/packages/nats-state-store/tsconfig.json b/packages/nats-state-store/tsconfig.json index 42dac6999..a2f689e2f 100644 --- a/packages/nats-state-store/tsconfig.json +++ b/packages/nats-state-store/tsconfig.json @@ -1,21 +1,13 @@ { - "extends": "../../tsconfig.json", - "compilerOptions": { - "outDir": "./dist", - "declaration": true, - "sourceMap": false, - "declarationMap": true, - "types": [ - "vitest/globals", - "node" - ] - }, - "exclude": [ - "./**/*.d.ts" - ], - - "include": [ - "./src/**/*", - "./test/*", - ], -} \ No newline at end of file + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "./dist", + "declaration": true, + "sourceMap": false, + "declarationMap": true, + "types": ["vitest/globals", "node"] + }, + "exclude": ["./**/*.d.ts"], + + "include": ["./src/**/*", "./test/*"] +} diff --git a/packages/nats-state-store/typedoc.json b/packages/nats-state-store/typedoc.json index 355bf0f98..71c4b2283 100644 --- a/packages/nats-state-store/typedoc.json +++ b/packages/nats-state-store/typedoc.json @@ -1,6 +1,5 @@ { - - "extends": ["../../typedoc.base.json"], - "entryPoints": ["src/index.ts"], - "tsconfig": "./tsconfig.json" -} \ No newline at end of file + "extends": ["../../typedoc.base.json"], + "entryPoints": ["src/index.ts"], + "tsconfig": "./tsconfig.json" +} diff --git a/packages/natsbridge/jsr.json b/packages/natsbridge/jsr.json new file mode 100644 index 000000000..b6c4237f9 --- /dev/null +++ b/packages/natsbridge/jsr.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://jsr.io/schema/config-file.v1.json", + "name": "@purista/natsbridge", + "version": "1.11.0", + "description": "NATS eventbridge for PURISTA backend framework", + "keywords": ["purista", "nats", "typescript", "javascript"], + "exports": "./dist/esm/index.js", + "publish": { + "include": ["dist/**/*.js", "dist/**/*.d.ts", "README.md", "package.json"], + "exclude": [ + "src", + ".github", + ".vscode", + ".zed", + "!dist", + "!dist/**/*.js", + "!dist/**/*.d.ts", + ".tshy", + ".tshy-build", + "vendor", + "docs", + "typedoc.json", + "..eslintcache", + ".npmignore" + ] + } +} diff --git a/packages/natsbridge/package.json b/packages/natsbridge/package.json index b14f4cd39..94ccecfb7 100644 --- a/packages/natsbridge/package.json +++ b/packages/natsbridge/package.json @@ -1,69 +1,66 @@ { - "name": "@purista/natsbridge", - "version": "1.11.0", - "description": "NATS eventbridge for PURISTA backend framework", - "homepage": "https://purista.dev", - "repository": { - "type": "git", - "url": "git@github.com:sebastianwessel/purista.git" - }, - "author": "Sebastian Wessel", - "license": "ISC", - "type": "module", - "main": "./dist/commonjs/index.js", - "exports": { - "./package.json": "./package.json", - ".": { - "import": { - "types": "./dist/esm/index.d.ts", - "default": "./dist/esm/index.js" - }, - "require": { - "types": "./dist/commonjs/index.d.ts", - "default": "./dist/commonjs/index.js" - } - } - }, - "files": [ - "dist/**/*" - ], - "publishConfig": { - "access": "public" - }, - "engines": { - "node": ">=18.15" - }, - "scripts": { - "lint": "eslint . --ext .ts,.json --cache . --fix", - "test": "vitest", - "build": "rimraf dist && tshy" - }, - "tshy": { - "exclude": [ - "src/**/*.test.ts" - ], - "exports": { - "./package.json": "./package.json", - ".": "./src/index.ts" - } - }, - "devDependencies": { - "@testcontainers/nats": "^10.6.0", - "@types/node": "^20.11.17", - "@types/sinon": "^17.0.3", - "@types/ws": "^8.5.10", - "sinon": "^17.0.1", - "tshy": "^1.11.1", - "vitest": "^1.3.0" - }, - "dependencies": { - "@opentelemetry/api": "^1.7.0", - "@opentelemetry/resources": "^1.19.0", - "@opentelemetry/sdk-trace-node": "^1.19.0", - "@opentelemetry/semantic-conventions": "^1.19.0", - "@purista/core": "*", - "nats": "^2.19.0" - }, - "peerDependenciesMeta": {}, - "types": "./dist/commonjs/index.d.ts" + "name": "@purista/natsbridge", + "version": "1.11.0", + "description": "NATS eventbridge for PURISTA backend framework", + "homepage": "https://purista.dev", + "repository": { + "type": "git", + "url": "git@github.com:puristajs/purista.git" + }, + "author": "Sebastian Wessel", + "license": "ISC", + "type": "module", + "main": "./dist/commonjs/index.js", + "exports": { + "./package.json": "./package.json", + ".": { + "import": { + "types": "./dist/esm/index.d.ts", + "default": "./dist/esm/index.js" + }, + "require": { + "types": "./dist/commonjs/index.d.ts", + "default": "./dist/commonjs/index.js" + } + } + }, + "files": ["dist/**/*"], + "publishConfig": { + "access": "public" + }, + "engines": { + "node": ">=18.15" + }, + "scripts": { + "lint": "npx @biomejs/biome check --write", + "test": "vitest", + "build": "rimraf dist && tshy" + }, + "tshy": { + "exclude": ["src/**/*.test.ts"], + "exports": { + "./package.json": "./package.json", + ".": "./src/index.ts" + } + }, + "devDependencies": { + "@testcontainers/nats": "^10.12.0", + "@types/node": "^22.5.1", + "@types/sinon": "^17.0.3", + "@types/ws": "^8.5.12", + "sinon": "^19.0.2", + "tshy": "^3.0.2", + "vitest": "^3.0.4" + }, + "dependencies": { + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/resources": "^1.26.0", + "@opentelemetry/sdk-trace-node": "^1.26.0", + "@opentelemetry/semantic-conventions": "^1.27.0", + "@purista/core": "*", + "nats": "^2.28.1" + }, + "peerDependenciesMeta": {}, + "types": "./dist/commonjs/index.d.ts", + "module": "./dist/esm/index.js" } diff --git a/packages/natsbridge/src/NatsBridge.ts b/packages/natsbridge/src/NatsBridge.ts index 74bf0c069..e8eed078d 100644 --- a/packages/natsbridge/src/NatsBridge.ts +++ b/packages/natsbridge/src/NatsBridge.ts @@ -1,39 +1,39 @@ import { SpanKind, SpanStatusCode } from '@opentelemetry/api' import type { - BrokerHeaderCommandMsg, - BrokerHeaderCustomMsg, - Command, - CommandDefinitionMetadataBase, - CommandErrorResponse, - CommandResponse, - CommandSuccessResponse, - CustomMessage, - DefinitionEventBridgeConfig, - EBMessage, - EBMessageAddress, - EventBridge, - EventBridgeConfig, - Subscription, + BrokerHeaderCommandMsg, + BrokerHeaderCustomMsg, + Command, + CommandDefinitionMetadataBase, + CommandErrorResponse, + CommandResponse, + CommandSuccessResponse, + CustomMessage, + DefinitionEventBridgeConfig, + EBMessage, + EBMessageAddress, + EventBridge, + EventBridgeConfig, + Subscription, } from '@purista/core' import { - createInfoMessage, - deserializeOtp, - EBMessageType, - EventBridgeBaseClass, - EventBridgeEventNames, - getNewCorrelationId, - getNewEBMessageId, - HandledError, - isCommandResponse, - isCommandSuccessResponse, - PuristaSpanName, - PuristaSpanTag, - serializeOtp, - StatusCode, - UnhandledError, + EBMessageType, + EventBridgeBaseClass, + EventBridgeEventNames, + HandledError, + PuristaSpanName, + PuristaSpanTag, + StatusCode, + UnhandledError, + createInfoMessage, + deserializeOtp, + getNewCorrelationId, + getNewEBMessageId, + isCommandResponse, + isCommandSuccessResponse, + serializeOtp, } from '@purista/core' import type { JetStreamManager, MsgHdrs, NatsConnection, Subscription as NatsSubscription } from 'nats' -import { connect, headers as getNewHeaders, JSONCodec } from 'nats' +import { JSONCodec, connect, headers as getNewHeaders } from 'nats' import { deserializeOtpFromNats } from './deserializeOtpFromNats.impl.js' import { getDefaultNatsBridgeConfig } from './getDefaultNatsBridgeConfig.js' @@ -62,333 +62,337 @@ import { NatsBridge } from '@purista/natsbridge' ``` */ export class NatsBridge extends EventBridgeBaseClass implements EventBridge { - public connection: NatsConnection | undefined - - public isJetStreamEnabled = false - - public jsm: JetStreamManager | undefined - - commands = new Map() - subscriptions = new Map() - - sc = JSONCodec() - - constructor(config?: EventBridgeConfig>) { - const conf = { - ...getDefaultNatsBridgeConfig(), - ...config, - } - - super('NatsBridge', conf) - } - - async start() { - const conf = { ...this.config, name: this.instanceId } - this.connection = await connect(conf) - - this.isJetStreamEnabled = !!this.connection.info?.jetstream - - if (this.isJetStreamEnabled) { - this.jsm = await this.connection.jetstreamManager() - } - - this.emit(EventBridgeEventNames.EventbridgeConnected) - } - - async isReady() { - return !this.connection?.isClosed() && !this.connection?.isDraining() - } - - async isHealthy() { - return !this.connection?.isClosed() && !this.connection?.isDraining() - } - - async emitMessage( - message: Omit, - contentType = 'application/json', - contentEncoding = 'utf-8', - ): Promise> { - if (!this.connection) { - throw new UnhandledError(StatusCode.ServiceUnavailable, 'not connected to a NATS server') - } - - const context = deserializeOtp(this.logger, message.otp) - - const name = isCommandResponse(message as EBMessage) - ? PuristaSpanName.EventBridgeCommandResponseSent - : PuristaSpanName.EventBridgeEmitMessage - - return this.startActiveSpan(name, { kind: SpanKind.PRODUCER }, context, async (span) => { - const msg = Object.freeze({ - ...message, - sender: { - ...message.sender, - instanceId: this.instanceId, - }, - id: getNewEBMessageId(), - timestamp: Date.now(), - traceId: message.traceId, - otp: serializeOtp(), - contentType, - contentEncoding, - }) as EBMessage - - span.setAttribute(PuristaSpanTag.SenderServiceName, msg.sender.serviceName) - span.setAttribute(PuristaSpanTag.SenderServiceVersion, msg.sender.serviceVersion) - span.setAttribute(PuristaSpanTag.SenderServiceTarget, msg.sender.serviceTarget) - - if (msg.eventName) { - span.addEvent(msg.eventName) - } - - let headers: MsgHdrs | undefined - if (this.connection?.info?.headers) { - headers = getNewHeaders() - const userProperties: BrokerHeaderCustomMsg = serializeOtpToNats({ - messageType: msg.messageType, - senderServiceName: msg.sender.serviceName, - senderServiceVersion: msg.sender.serviceVersion, - senderServiceTarget: msg.sender.serviceTarget, - senderInstanceId: msg.sender.instanceId, - }) - - if (msg.eventName) { - userProperties.eventName = msg.eventName - } - - if (msg.principalId) { - userProperties.principalId = msg.principalId - } - - if (msg.tenantId) { - userProperties.tenantId = msg.tenantId - } - - Object.entries(userProperties).forEach((value) => headers?.set(value[0], value[1])) - } - const topic = getTopicName.bind(this)(msg) - - this.connection?.publish(topic, this.sc.encode(msg), { headers }) - - return msg as Readonly - }) - } - - async invoke( - input: Omit, - commandTimeout: number = this.defaultCommandTimeout, - ): Promise { - if (!this.connection) { - throw new UnhandledError(StatusCode.ServiceUnavailable, 'not connected to a NATS server') - } - - const context = deserializeOtp(this.logger, input.otp) - return this.startActiveSpan( - PuristaSpanName.EventBridgeInvokeCommand, - { kind: SpanKind.PRODUCER }, - context, - async (span) => { - const correlationId = getNewCorrelationId() - - if (!this.connection) { - throw new UnhandledError(StatusCode.ServiceUnavailable, 'not connected to a NATS server') - } - - const command: Command = Object.freeze({ - ...input, - sender: { - ...input.sender, - instanceId: this.instanceId, - }, - id: getNewEBMessageId(), - correlationId, - timestamp: Date.now(), - messageType: EBMessageType.Command, - traceId: input.traceId, - otp: serializeOtp(), - }) - - const log = this.logger.getChildLogger({ ...span.spanContext(), customTraceId: command.traceId }) - - span.setAttribute(PuristaSpanTag.SenderServiceName, command.sender.serviceName) - span.setAttribute(PuristaSpanTag.SenderServiceVersion, command.sender.serviceVersion) - span.setAttribute(PuristaSpanTag.SenderServiceTarget, command.sender.serviceTarget) - span.setAttribute(PuristaSpanTag.ReceiverServiceName, command.receiver.serviceName) - span.setAttribute(PuristaSpanTag.ReceiverServiceVersion, command.receiver.serviceVersion) - span.setAttribute(PuristaSpanTag.ReceiverServiceTarget, command.receiver.serviceTarget) - - let headers: MsgHdrs | undefined - if (this.connection?.info?.headers) { - headers = getNewHeaders() - const userProperties: BrokerHeaderCommandMsg = serializeOtpToNats({ - messageType: command.messageType, - senderServiceName: command.sender.serviceName, - senderServiceVersion: command.sender.serviceVersion, - senderServiceTarget: command.sender.serviceTarget, - senderInstanceId: command.sender.instanceId, - receiverServiceName: command.receiver.serviceName, - receiverServiceVersion: command.receiver.serviceVersion, - receiverServiceTarget: command.receiver.serviceTarget, - }) - - if (command.eventName) { - userProperties.eventName = command.eventName - } - - if (command.receiver.instanceId) { - userProperties.receiverInstanceId = command.receiver.instanceId - } - - if (command.principalId) { - userProperties.principalId = command.principalId - } - - if (command.tenantId) { - userProperties.tenantId = command.tenantId - } - - Object.entries(userProperties).forEach((value) => headers?.set(value[0], value[1])) - } - - const topic = getTopicName.bind(this)(command) - - const data = this.sc.encode(command) - - try { - const msg = await this.connection.request(topic, data, { - timeout: commandTimeout, - }) - - const response: CommandResponse = this.sc.decode(msg.data) as CommandResponse - const returnContext = deserializeOtpFromNats(this.logger, response, msg.headers) - return this.startActiveSpan( - PuristaSpanName.EventBridgeCommandResponseReceived, - { kind: SpanKind.CONSUMER }, - returnContext, - async (returnSpan) => { - const responseLog = this.logger.getChildLogger({ ...span.spanContext(), customTraceId: response.traceId }) - - if (response.eventName) { - returnSpan.addEvent(response.eventName) - } - - if (!isCommandResponse(response)) { - const err = new UnhandledError( - StatusCode.InternalServerError, - 'the received message is not a command response', - ) - responseLog.error({ err }, err.message) - returnSpan.setStatus({ - code: SpanStatusCode.ERROR, - message: err.message, - }) - returnSpan.recordException(err) - this.emit(EventBridgeEventNames.EventbridgeError, err) - throw err - } - - if (isCommandSuccessResponse(response)) { - return response.payload as T - } - const error = response.isHandledError - ? HandledError.fromMessage(response) - : UnhandledError.fromMessage(response) - - returnSpan.setStatus({ - code: SpanStatusCode.ERROR, - message: error.message, - }) - returnSpan.recordException(error) - responseLog.error({ err: error }, error.message) - throw error - }, - ) - } catch (error) { - if (error instanceof HandledError || error instanceof UnhandledError) { - throw error - } - const err = UnhandledError.fromError(error) - log.error({ err }) - throw err - } - }, - ) - } - - async registerCommand( - address: EBMessageAddress, - cb: (message: Command) => Promise, - metadata: CommandDefinitionMetadataBase, - eventBridgeConfig: DefinitionEventBridgeConfig, - ): Promise { - if (!this.connection) { - throw new UnhandledError(StatusCode.ServiceUnavailable, 'not connected to a NATS server') - } - - const topic = getCommandSubscriptionTopic.bind(this)(address) - - const callback = getCommandHandler(address, cb, metadata, eventBridgeConfig).bind(this) - const queue = getQueueGroupName(this.config.topicPrefix, address) - const subscription = this.connection.subscribe(topic, { callback, queue }) - - this.commands.set(`${address.serviceName}-${address.serviceVersion},${address.serviceTarget}`, subscription) - - const info = createInfoMessage( - EBMessageType.InfoServiceFunctionAdded, - { ...address, instanceId: this.instanceId }, - { - payload: metadata, - }, - ) - await this.emitMessage(info) - - return topic - } - - async unregisterCommand(address: EBMessageAddress): Promise { - if (!this.connection) { - throw new UnhandledError(StatusCode.ServiceUnavailable, 'not connected to a NATS server') - } - - const subscription = this.commands.get(`${address.serviceName}-${address.serviceVersion},${address.serviceTarget}`) - - subscription?.unsubscribe() - await subscription?.drain() - } - - async registerSubscription( - subscription: Subscription, - cb: (message: EBMessage) => Promise | undefined>, - ): Promise { - const topic = getSubscriptionTopic.bind(this)(subscription) - - const queueName = getQueueGroupName(this.config.topicPrefix, subscription.subscriber) - const queue = subscription.eventBridgeConfig.shared ? queueName : undefined - this.connection?.subscribe(topic, { - callback: getSubscriptionHandler(subscription, cb).bind(this), - queue, - }) - - return topic - } - - async unregisterSubscription(address: EBMessageAddress): Promise { - if (!this.connection) { - throw new UnhandledError(StatusCode.ServiceUnavailable, 'not connected to a NATS server') - } - - const subscription = this.subscriptions.get( - `${address.serviceName}-${address.serviceVersion},${address.serviceTarget}`, - ) - - subscription?.unsubscribe() - await subscription?.drain() - } - - async destroy() { - await this.connection?.drain() - await this.connection?.close() - this.emit(EventBridgeEventNames.EventbridgeDisconnected) - await super.destroy() - } + public connection: NatsConnection | undefined + + public isJetStreamEnabled = false + + public jsm: JetStreamManager | undefined + + commands = new Map() + subscriptions = new Map() + + sc = JSONCodec() + + constructor(config?: EventBridgeConfig>) { + const conf = { + ...getDefaultNatsBridgeConfig(), + ...config, + } + + super('NatsBridge', conf) + } + + async start() { + const conf = { ...this.config, name: this.instanceId } + this.connection = await connect(conf) + + this.isJetStreamEnabled = !!this.connection.info?.jetstream + + if (this.isJetStreamEnabled) { + this.jsm = await this.connection.jetstreamManager() + } + + this.emit(EventBridgeEventNames.EventbridgeConnected) + } + + async isReady() { + return !this.connection?.isClosed() && !this.connection?.isDraining() + } + + async isHealthy() { + return !this.connection?.isClosed() && !this.connection?.isDraining() + } + + async emitMessage( + message: Omit, + contentType = 'application/json', + contentEncoding = 'utf-8', + ): Promise> { + if (!this.connection) { + throw new UnhandledError(StatusCode.ServiceUnavailable, 'not connected to a NATS server') + } + + const context = deserializeOtp(this.logger, message.otp) + + const name = isCommandResponse(message as EBMessage) + ? PuristaSpanName.EventBridgeCommandResponseSent + : PuristaSpanName.EventBridgeEmitMessage + + return this.startActiveSpan(name, { kind: SpanKind.PRODUCER }, context, async span => { + const msg = Object.freeze({ + ...message, + sender: { + ...message.sender, + instanceId: this.instanceId, + }, + id: getNewEBMessageId(), + timestamp: Date.now(), + traceId: message.traceId, + otp: serializeOtp(), + contentType, + contentEncoding, + }) as EBMessage + + span.setAttribute(PuristaSpanTag.SenderServiceName, msg.sender.serviceName) + span.setAttribute(PuristaSpanTag.SenderServiceVersion, msg.sender.serviceVersion) + span.setAttribute(PuristaSpanTag.SenderServiceTarget, msg.sender.serviceTarget) + + if (msg.eventName) { + span.addEvent(msg.eventName) + } + + let headers: MsgHdrs | undefined + if (this.connection?.info?.headers) { + headers = getNewHeaders() + const userProperties: BrokerHeaderCustomMsg = serializeOtpToNats({ + messageType: msg.messageType, + senderServiceName: msg.sender.serviceName, + senderServiceVersion: msg.sender.serviceVersion, + senderServiceTarget: msg.sender.serviceTarget, + senderInstanceId: msg.sender.instanceId, + }) + + if (msg.eventName) { + userProperties.eventName = msg.eventName + } + + if (msg.principalId) { + userProperties.principalId = msg.principalId + } + + if (msg.tenantId) { + userProperties.tenantId = msg.tenantId + } + + for (const value of Object.entries(userProperties)) { + headers?.set(value[0], value[1]) + } + } + const topic = getTopicName.bind(this)(msg) + + this.connection?.publish(topic, this.sc.encode(msg), { headers }) + + return msg as Readonly + }) + } + + async invoke( + input: Omit, + commandTimeout: number = this.defaultCommandTimeout, + ): Promise { + if (!this.connection) { + throw new UnhandledError(StatusCode.ServiceUnavailable, 'not connected to a NATS server') + } + + const context = deserializeOtp(this.logger, input.otp) + return this.startActiveSpan( + PuristaSpanName.EventBridgeInvokeCommand, + { kind: SpanKind.PRODUCER }, + context, + async span => { + const correlationId = getNewCorrelationId() + + if (!this.connection) { + throw new UnhandledError(StatusCode.ServiceUnavailable, 'not connected to a NATS server') + } + + const command: Command = Object.freeze({ + ...input, + sender: { + ...input.sender, + instanceId: this.instanceId, + }, + id: getNewEBMessageId(), + correlationId, + timestamp: Date.now(), + messageType: EBMessageType.Command, + traceId: input.traceId, + otp: serializeOtp(), + }) + + const log = this.logger.getChildLogger({ ...span.spanContext(), customTraceId: command.traceId }) + + span.setAttribute(PuristaSpanTag.SenderServiceName, command.sender.serviceName) + span.setAttribute(PuristaSpanTag.SenderServiceVersion, command.sender.serviceVersion) + span.setAttribute(PuristaSpanTag.SenderServiceTarget, command.sender.serviceTarget) + span.setAttribute(PuristaSpanTag.ReceiverServiceName, command.receiver.serviceName) + span.setAttribute(PuristaSpanTag.ReceiverServiceVersion, command.receiver.serviceVersion) + span.setAttribute(PuristaSpanTag.ReceiverServiceTarget, command.receiver.serviceTarget) + + let headers: MsgHdrs | undefined + if (this.connection?.info?.headers) { + headers = getNewHeaders() + const userProperties: BrokerHeaderCommandMsg = serializeOtpToNats({ + messageType: command.messageType, + senderServiceName: command.sender.serviceName, + senderServiceVersion: command.sender.serviceVersion, + senderServiceTarget: command.sender.serviceTarget, + senderInstanceId: command.sender.instanceId, + receiverServiceName: command.receiver.serviceName, + receiverServiceVersion: command.receiver.serviceVersion, + receiverServiceTarget: command.receiver.serviceTarget, + }) + + if (command.eventName) { + userProperties.eventName = command.eventName + } + + if (command.receiver.instanceId) { + userProperties.receiverInstanceId = command.receiver.instanceId + } + + if (command.principalId) { + userProperties.principalId = command.principalId + } + + if (command.tenantId) { + userProperties.tenantId = command.tenantId + } + + for (const value of Object.entries(userProperties)) { + headers?.set(value[0], value[1]) + } + } + + const topic = getTopicName.bind(this)(command) + + const data = this.sc.encode(command) + + try { + const msg = await this.connection.request(topic, data, { + timeout: commandTimeout, + }) + + const response: CommandResponse = this.sc.decode(msg.data) as CommandResponse + const returnContext = deserializeOtpFromNats(this.logger, response, msg.headers) + return this.startActiveSpan( + PuristaSpanName.EventBridgeCommandResponseReceived, + { kind: SpanKind.CONSUMER }, + returnContext, + async returnSpan => { + const responseLog = this.logger.getChildLogger({ ...span.spanContext(), customTraceId: response.traceId }) + + if (response.eventName) { + returnSpan.addEvent(response.eventName) + } + + if (!isCommandResponse(response)) { + const err = new UnhandledError( + StatusCode.InternalServerError, + 'the received message is not a command response', + ) + responseLog.error({ err }, err.message) + returnSpan.setStatus({ + code: SpanStatusCode.ERROR, + message: err.message, + }) + returnSpan.recordException(err) + this.emit(EventBridgeEventNames.EventbridgeError, err) + throw err + } + + if (isCommandSuccessResponse(response)) { + return response.payload as T + } + const error = response.isHandledError + ? HandledError.fromMessage(response) + : UnhandledError.fromMessage(response) + + returnSpan.setStatus({ + code: SpanStatusCode.ERROR, + message: error.message, + }) + returnSpan.recordException(error) + responseLog.error({ err: error }, error.message) + throw error + }, + ) + } catch (error) { + if (error instanceof HandledError || error instanceof UnhandledError) { + throw error + } + const err = UnhandledError.fromError(error) + log.error({ err }) + throw err + } + }, + ) + } + + async registerCommand( + address: EBMessageAddress, + cb: (message: Command) => Promise, + metadata: CommandDefinitionMetadataBase, + eventBridgeConfig: DefinitionEventBridgeConfig, + ): Promise { + if (!this.connection) { + throw new UnhandledError(StatusCode.ServiceUnavailable, 'not connected to a NATS server') + } + + const topic = getCommandSubscriptionTopic.bind(this)(address) + + const callback = getCommandHandler(address, cb, metadata, eventBridgeConfig).bind(this) + const queue = getQueueGroupName(this.config.topicPrefix, address) + const subscription = this.connection.subscribe(topic, { callback, queue }) + + this.commands.set(`${address.serviceName}-${address.serviceVersion},${address.serviceTarget}`, subscription) + + const info = createInfoMessage( + EBMessageType.InfoServiceFunctionAdded, + { ...address, instanceId: this.instanceId }, + { + payload: metadata, + }, + ) + await this.emitMessage(info) + + return topic + } + + async unregisterCommand(address: EBMessageAddress): Promise { + if (!this.connection) { + throw new UnhandledError(StatusCode.ServiceUnavailable, 'not connected to a NATS server') + } + + const subscription = this.commands.get(`${address.serviceName}-${address.serviceVersion},${address.serviceTarget}`) + + subscription?.unsubscribe() + await subscription?.drain() + } + + async registerSubscription( + subscription: Subscription, + cb: (message: EBMessage) => Promise | undefined>, + ): Promise { + const topic = getSubscriptionTopic.bind(this)(subscription) + + const queueName = getQueueGroupName(this.config.topicPrefix, subscription.subscriber) + const queue = subscription.eventBridgeConfig.shared ? queueName : undefined + this.connection?.subscribe(topic, { + callback: getSubscriptionHandler(subscription, cb).bind(this), + queue, + }) + + return topic + } + + async unregisterSubscription(address: EBMessageAddress): Promise { + if (!this.connection) { + throw new UnhandledError(StatusCode.ServiceUnavailable, 'not connected to a NATS server') + } + + const subscription = this.subscriptions.get( + `${address.serviceName}-${address.serviceVersion},${address.serviceTarget}`, + ) + + subscription?.unsubscribe() + await subscription?.drain() + } + + async destroy() { + await this.connection?.drain() + await this.connection?.close() + this.emit(EventBridgeEventNames.EventbridgeDisconnected) + await super.destroy() + } } diff --git a/packages/natsbridge/src/deserializeOtpFromNats.impl.ts b/packages/natsbridge/src/deserializeOtpFromNats.impl.ts index 3f20a722f..94d2f2ea3 100644 --- a/packages/natsbridge/src/deserializeOtpFromNats.impl.ts +++ b/packages/natsbridge/src/deserializeOtpFromNats.impl.ts @@ -4,10 +4,10 @@ import { deserializeOtp } from '@purista/core' import type { MsgHdrs } from 'nats' export const deserializeOtpFromNats = (logger: Logger, message: EBMessage, headers?: MsgHdrs) => { - // try to use mqtt user property first - if (headers?.has('traceparent')) { - return propagation.extract(context.active(), headers) - } + // try to use mqtt user property first + if (headers?.has('traceparent')) { + return propagation.extract(context.active(), headers) + } - return deserializeOtp(logger, message.otp) + return deserializeOtp(logger, message.otp) } diff --git a/packages/natsbridge/src/getDefaultNatsBridgeConfig.ts b/packages/natsbridge/src/getDefaultNatsBridgeConfig.ts index 79c071c3d..5c5ddb47d 100644 --- a/packages/natsbridge/src/getDefaultNatsBridgeConfig.ts +++ b/packages/natsbridge/src/getDefaultNatsBridgeConfig.ts @@ -2,11 +2,11 @@ import type { NatsBridgeConfig } from './types/index.js' const SECONDS_PER_DAY = 86_400 export const getDefaultNatsBridgeConfig = (): NatsBridgeConfig => { - return { - topicPrefix: 'purista', - emptyTopicPartString: '__empty__', - commandResponsePublishTwice: 'eventOnly', - defaultMessageExpiryInterval: 30 * SECONDS_PER_DAY, - maxMessages: 10, - } + return { + topicPrefix: 'purista', + emptyTopicPartString: '__empty__', + commandResponsePublishTwice: 'eventOnly', + defaultMessageExpiryInterval: 30 * SECONDS_PER_DAY, + maxMessages: 10, + } } diff --git a/packages/natsbridge/src/getQueueGroupName.impl.ts b/packages/natsbridge/src/getQueueGroupName.impl.ts index 5ccecb869..acb514643 100644 --- a/packages/natsbridge/src/getQueueGroupName.impl.ts +++ b/packages/natsbridge/src/getQueueGroupName.impl.ts @@ -1,4 +1,4 @@ import type { EBMessageAddress } from '@purista/core' export const getQueueGroupName = (prefix: string, address: EBMessageAddress) => - `${prefix}_queue_${address.serviceName}_${address.serviceVersion}_${address.serviceTarget}`.toUpperCase() + `${prefix}_queue_${address.serviceName}_${address.serviceVersion}_${address.serviceTarget}`.toUpperCase() diff --git a/packages/natsbridge/src/handler/getCommandHandler.impl.ts b/packages/natsbridge/src/handler/getCommandHandler.impl.ts index 77243e535..349fceec2 100644 --- a/packages/natsbridge/src/handler/getCommandHandler.impl.ts +++ b/packages/natsbridge/src/handler/getCommandHandler.impl.ts @@ -1,23 +1,23 @@ import { SpanKind, SpanStatusCode } from '@opentelemetry/api' import type { - BrokerHeaderCommandResponseMsg, - Command, - CommandDefinitionMetadataBase, - CommandErrorResponse, - CommandSuccessResponse, - DefinitionEventBridgeConfig, - EBMessageAddress, + BrokerHeaderCommandResponseMsg, + Command, + CommandDefinitionMetadataBase, + CommandErrorResponse, + CommandSuccessResponse, + DefinitionEventBridgeConfig, + EBMessageAddress, } from '@purista/core' import { - deserializeOtp, - EventBridgeEventNames, - isCommand, - isCommandErrorResponse, - PuristaSpanName, - PuristaSpanTag, - serializeOtp, - StatusCode, - UnhandledError, + EventBridgeEventNames, + PuristaSpanName, + PuristaSpanTag, + StatusCode, + UnhandledError, + deserializeOtp, + isCommand, + isCommandErrorResponse, + serializeOtp, } from '@purista/core' import type { MsgHdrs } from 'nats' import { headers as getNewHeaders } from 'nats' @@ -28,140 +28,142 @@ import { getTopicName } from '../topic/index.js' import type { IncomingMessageFunction } from '../types/index.js' export const getCommandHandler = ( - address: EBMessageAddress, - cb: (message: Command) => Promise, - _metadata: CommandDefinitionMetadataBase, - _eventBridgeConfig: DefinitionEventBridgeConfig, + address: EBMessageAddress, + cb: (message: Command) => Promise, + _metadata: CommandDefinitionMetadataBase, + _eventBridgeConfig: DefinitionEventBridgeConfig, ) => { - const handleCommand: IncomingMessageFunction = async function (error, msg) { - if (error) { - const err = UnhandledError.fromError(error) - this.logger.error({ err, address }, `error in command subscription: ${err.message}`) - return - } - - let command: Command - try { - command = this.sc.decode(msg.data) as Command - } catch (err) { - this.logger.error({ err, address }, `error in command subscription - unable to extract payload`) - return - } - - const context = deserializeOtpFromNats(this.logger, command, msg.headers) - return this.startActiveSpan( - PuristaSpanName.EventBridgeCommandReceived, - { kind: SpanKind.CONSUMER }, - context, - async (span) => { - const log = this.logger.getChildLogger({ ...span.spanContext(), customTraceId: command.traceId }) - try { - if (!isCommand(command)) { - const err = new UnhandledError(StatusCode.InternalServerError, 'expected a command message') - log.error({ err }, err.message) - span.setStatus({ - code: SpanStatusCode.ERROR, - message: err.message, - }) - span.recordException(err) - this.emit(EventBridgeEventNames.EventbridgeError, err) - return - } - - const result = await cb(command) - - const returnContext = deserializeOtp(log, result.otp) - return this.startActiveSpan( - PuristaSpanName.EventBridgeCommandResponseSent, - { kind: SpanKind.PRODUCER }, - returnContext, - async (subSpan) => { - const responseMessage = { - ...result, - sender: { - ...result.sender, - instanceId: this.instanceId, - }, - otp: result.otp ?? serializeOtp(), - } - - subSpan.setAttribute(PuristaSpanTag.SenderServiceName, responseMessage.sender.serviceName) - subSpan.setAttribute(PuristaSpanTag.SenderServiceVersion, responseMessage.sender.serviceVersion) - subSpan.setAttribute(PuristaSpanTag.SenderServiceTarget, responseMessage.sender.serviceTarget) - - if (responseMessage.eventName) { - subSpan.addEvent(responseMessage.eventName) - } - - let headers: MsgHdrs | undefined - if (this.connection?.info?.headers) { - headers = getNewHeaders() - - const userProperties: BrokerHeaderCommandResponseMsg = serializeOtpToNats({ - messageType: responseMessage.messageType, - senderServiceName: responseMessage.sender.serviceName, - senderServiceVersion: responseMessage.sender.serviceVersion, - senderServiceTarget: responseMessage.sender.serviceTarget, - senderInstanceId: responseMessage.sender.instanceId, - receiverServiceName: responseMessage.receiver.serviceName, - receiverServiceVersion: responseMessage.receiver.serviceVersion, - receiverServiceTarget: responseMessage.receiver.serviceTarget, - receiverInstanceId: responseMessage.receiver.instanceId, - }) - - if (responseMessage.eventName) { - userProperties.eventName = responseMessage.eventName - } - - if (responseMessage.principalId) { - userProperties.principalId = responseMessage.principalId - } - - if (responseMessage.tenantId) { - userProperties.tenantId = responseMessage.tenantId - } - - Object.entries(userProperties).forEach((value) => headers?.set(value[0], value[1])) - } - - const responseData = this.sc.encode(responseMessage) - - msg.respond(responseData, { - headers, - }) - - if (this.config.commandResponsePublishTwice === 'never') { - return - } - - // emit the message 2nd time as event - if ( - (this.config.commandResponsePublishTwice === 'always' || - (responseMessage.eventName && this.config.commandResponsePublishTwice === 'eventOnly')) ?? - (isCommandErrorResponse(responseMessage) && this.config.commandResponsePublishTwice === 'eventAndError') - ) { - const eventTopic = getTopicName.bind(this)(responseMessage) - - this.connection?.publish(eventTopic, responseData, { headers }) - } - }, - ) - } catch (error) { - const err = new UnhandledError(StatusCode.InternalServerError, 'Failed to consume command response message', { - error, - }) - span.setStatus({ - code: SpanStatusCode.ERROR, - message: err.message, - }) - span.recordException(err) - this.emit(EventBridgeEventNames.EventbridgeError, err) - log.error({ err }, 'Failed to consume command response message') - throw err - } - }, - ) - } - - return handleCommand + const handleCommand: IncomingMessageFunction = async function (error, msg) { + if (error) { + const err = UnhandledError.fromError(error) + this.logger.error({ err, address }, `error in command subscription: ${err.message}`) + return + } + + let command: Command + try { + command = this.sc.decode(msg.data) as Command + } catch (err) { + this.logger.error({ err, address }, 'error in command subscription - unable to extract payload') + return + } + + const context = deserializeOtpFromNats(this.logger, command, msg.headers) + return this.startActiveSpan( + PuristaSpanName.EventBridgeCommandReceived, + { kind: SpanKind.CONSUMER }, + context, + async span => { + const log = this.logger.getChildLogger({ ...span.spanContext(), customTraceId: command.traceId }) + try { + if (!isCommand(command)) { + const err = new UnhandledError(StatusCode.InternalServerError, 'expected a command message') + log.error({ err }, err.message) + span.setStatus({ + code: SpanStatusCode.ERROR, + message: err.message, + }) + span.recordException(err) + this.emit(EventBridgeEventNames.EventbridgeError, err) + return + } + + const result = await cb(command) + + const returnContext = deserializeOtp(log, result.otp) + return this.startActiveSpan( + PuristaSpanName.EventBridgeCommandResponseSent, + { kind: SpanKind.PRODUCER }, + returnContext, + async subSpan => { + const responseMessage = { + ...result, + sender: { + ...result.sender, + instanceId: this.instanceId, + }, + otp: result.otp ?? serializeOtp(), + } + + subSpan.setAttribute(PuristaSpanTag.SenderServiceName, responseMessage.sender.serviceName) + subSpan.setAttribute(PuristaSpanTag.SenderServiceVersion, responseMessage.sender.serviceVersion) + subSpan.setAttribute(PuristaSpanTag.SenderServiceTarget, responseMessage.sender.serviceTarget) + + if (responseMessage.eventName) { + subSpan.addEvent(responseMessage.eventName) + } + + let headers: MsgHdrs | undefined + if (this.connection?.info?.headers) { + headers = getNewHeaders() + + const userProperties: BrokerHeaderCommandResponseMsg = serializeOtpToNats({ + messageType: responseMessage.messageType, + senderServiceName: responseMessage.sender.serviceName, + senderServiceVersion: responseMessage.sender.serviceVersion, + senderServiceTarget: responseMessage.sender.serviceTarget, + senderInstanceId: responseMessage.sender.instanceId, + receiverServiceName: responseMessage.receiver.serviceName, + receiverServiceVersion: responseMessage.receiver.serviceVersion, + receiverServiceTarget: responseMessage.receiver.serviceTarget, + receiverInstanceId: responseMessage.receiver.instanceId, + }) + + if (responseMessage.eventName) { + userProperties.eventName = responseMessage.eventName + } + + if (responseMessage.principalId) { + userProperties.principalId = responseMessage.principalId + } + + if (responseMessage.tenantId) { + userProperties.tenantId = responseMessage.tenantId + } + + for (const value of Object.entries(userProperties)) { + headers?.set(value[0], value[1]) + } + } + + const responseData = this.sc.encode(responseMessage) + + msg.respond(responseData, { + headers, + }) + + if (this.config.commandResponsePublishTwice === 'never') { + return + } + + // emit the message 2nd time as event + if ( + (this.config.commandResponsePublishTwice === 'always' || + (responseMessage.eventName && this.config.commandResponsePublishTwice === 'eventOnly')) ?? + (isCommandErrorResponse(responseMessage) && this.config.commandResponsePublishTwice === 'eventAndError') + ) { + const eventTopic = getTopicName.bind(this)(responseMessage) + + this.connection?.publish(eventTopic, responseData, { headers }) + } + }, + ) + } catch (error) { + const err = new UnhandledError(StatusCode.InternalServerError, 'Failed to consume command response message', { + error, + }) + span.setStatus({ + code: SpanStatusCode.ERROR, + message: err.message, + }) + span.recordException(err) + this.emit(EventBridgeEventNames.EventbridgeError, err) + log.error({ err }, 'Failed to consume command response message') + throw err + } + }, + ) + } + + return handleCommand } diff --git a/packages/natsbridge/src/handler/getSubscriptionHandler.impl.ts b/packages/natsbridge/src/handler/getSubscriptionHandler.impl.ts index 49f86d606..e8dd527d2 100644 --- a/packages/natsbridge/src/handler/getSubscriptionHandler.impl.ts +++ b/packages/natsbridge/src/handler/getSubscriptionHandler.impl.ts @@ -1,117 +1,117 @@ import { SpanKind, SpanStatusCode } from '@opentelemetry/api' import type { BrokerHeaderCustomMsg, CustomMessage, EBMessage, Subscription } from '@purista/core' import { - deserializeOtp, - EventBridgeEventNames, - PuristaSpanName, - PuristaSpanTag, - serializeOtp, - StatusCode, - UnhandledError, + EventBridgeEventNames, + PuristaSpanName, + PuristaSpanTag, + StatusCode, + UnhandledError, + deserializeOtp, + serializeOtp, } from '@purista/core' import type { JsMsg, Msg, NatsError } from 'nats' -import { deserializeOtpFromNats } from '../deserializeOtpFromNats.impl.js' import type { NatsBridge } from '../NatsBridge.js' +import { deserializeOtpFromNats } from '../deserializeOtpFromNats.impl.js' import { serializeOtpToNats } from '../serializeOtpToNats.impl.js' import { getTopicName } from '../topic/index.js' export const getSubscriptionHandler = ( - subscription: Subscription, - cb: (message: EBMessage) => Promise | undefined>, + subscription: Subscription, + cb: (message: EBMessage) => Promise | undefined>, ) => { - const handler = async function (this: NatsBridge, error: NatsError | null, msg: Msg | JsMsg) { - if (error) { - const err = UnhandledError.fromError(error) - this.logger.error({ err, address: subscription.receiver }, `error in subscription: ${err.message}`) - return - } - - let message: EBMessage - try { - message = this.sc.decode(msg.data) as EBMessage - } catch (err) { - this.logger.error({ err, address: subscription.receiver }, `error in subscription - unable to extract payload`) - return - } - - const context = deserializeOtpFromNats(this.logger, message, msg.headers) - return this.startActiveSpan( - PuristaSpanName.EventBridgeCommandReceived, - { kind: SpanKind.CONSUMER }, - context, - async (span) => { - const log = this.logger.getChildLogger({ ...span.spanContext(), customTraceId: message.traceId }) - - try { - const result = await cb(message) - - if (!result) { - return - } - - const returnContext = deserializeOtp(log, result.otp) - return this.startActiveSpan( - PuristaSpanName.EventBridgeCommandResponseSent, - { kind: SpanKind.PRODUCER }, - returnContext, - async (subSpan) => { - const responseMessage = { - ...result, - sender: { - ...result.sender, - instanceId: this.instanceId, - }, - otp: serializeOtp(), - } - - subSpan.setAttribute(PuristaSpanTag.SenderServiceName, responseMessage.sender.serviceName) - subSpan.setAttribute(PuristaSpanTag.SenderServiceVersion, responseMessage.sender.serviceVersion) - subSpan.setAttribute(PuristaSpanTag.SenderServiceTarget, responseMessage.sender.serviceTarget) - - if (!responseMessage.eventName) { - return - } - - subSpan.addEvent(responseMessage.eventName) - - const userProperties: BrokerHeaderCustomMsg = serializeOtpToNats({ - messageType: responseMessage.messageType, - senderServiceName: responseMessage.sender.serviceName, - senderServiceVersion: responseMessage.sender.serviceVersion, - senderServiceTarget: responseMessage.sender.serviceTarget, - senderInstanceId: responseMessage.sender.instanceId, - eventName: responseMessage.eventName, - }) - - if (responseMessage.principalId) { - userProperties.principalId = responseMessage.principalId - } - - if (responseMessage.tenantId) { - userProperties.tenantId = responseMessage.tenantId - } - - const topic = getTopicName.bind(this)(responseMessage as EBMessage) - - this.connection?.publish(topic, this.sc.encode(responseMessage)) - }, - ) - } catch (error) { - const err = new UnhandledError(StatusCode.InternalServerError, 'Failed to consume subscription message', { - error, - }) - span.setStatus({ - code: SpanStatusCode.ERROR, - message: err.message, - }) - span.recordException(err) - this.emit(EventBridgeEventNames.EventbridgeError, err) - log.error({ err }, 'Failed to consume subscription message') - } - }, - ) - } - - return handler + const handler = async function (this: NatsBridge, error: NatsError | null, msg: Msg | JsMsg) { + if (error) { + const err = UnhandledError.fromError(error) + this.logger.error({ err, address: subscription.receiver }, `error in subscription: ${err.message}`) + return + } + + let message: EBMessage + try { + message = this.sc.decode(msg.data) as EBMessage + } catch (err) { + this.logger.error({ err, address: subscription.receiver }, 'error in subscription - unable to extract payload') + return + } + + const context = deserializeOtpFromNats(this.logger, message, msg.headers) + return this.startActiveSpan( + PuristaSpanName.EventBridgeCommandReceived, + { kind: SpanKind.CONSUMER }, + context, + async span => { + const log = this.logger.getChildLogger({ ...span.spanContext(), customTraceId: message.traceId }) + + try { + const result = await cb(message) + + if (!result) { + return + } + + const returnContext = deserializeOtp(log, result.otp) + return this.startActiveSpan( + PuristaSpanName.EventBridgeCommandResponseSent, + { kind: SpanKind.PRODUCER }, + returnContext, + async subSpan => { + const responseMessage = { + ...result, + sender: { + ...result.sender, + instanceId: this.instanceId, + }, + otp: serializeOtp(), + } + + subSpan.setAttribute(PuristaSpanTag.SenderServiceName, responseMessage.sender.serviceName) + subSpan.setAttribute(PuristaSpanTag.SenderServiceVersion, responseMessage.sender.serviceVersion) + subSpan.setAttribute(PuristaSpanTag.SenderServiceTarget, responseMessage.sender.serviceTarget) + + if (!responseMessage.eventName) { + return + } + + subSpan.addEvent(responseMessage.eventName) + + const userProperties: BrokerHeaderCustomMsg = serializeOtpToNats({ + messageType: responseMessage.messageType, + senderServiceName: responseMessage.sender.serviceName, + senderServiceVersion: responseMessage.sender.serviceVersion, + senderServiceTarget: responseMessage.sender.serviceTarget, + senderInstanceId: responseMessage.sender.instanceId, + eventName: responseMessage.eventName, + }) + + if (responseMessage.principalId) { + userProperties.principalId = responseMessage.principalId + } + + if (responseMessage.tenantId) { + userProperties.tenantId = responseMessage.tenantId + } + + const topic = getTopicName.bind(this)(responseMessage as EBMessage) + + this.connection?.publish(topic, this.sc.encode(responseMessage)) + }, + ) + } catch (error) { + const err = new UnhandledError(StatusCode.InternalServerError, 'Failed to consume subscription message', { + error, + }) + span.setStatus({ + code: SpanStatusCode.ERROR, + message: err.message, + }) + span.recordException(err) + this.emit(EventBridgeEventNames.EventbridgeError, err) + log.error({ err }, 'Failed to consume subscription message') + } + }, + ) + } + + return handler } diff --git a/packages/natsbridge/src/index.test.ts b/packages/natsbridge/src/index.test.ts index 1a87112fd..43d23a222 100644 --- a/packages/natsbridge/src/index.test.ts +++ b/packages/natsbridge/src/index.test.ts @@ -1,39 +1,39 @@ import { - getCommandSubscriptionTopic, - getDefaultNatsBridgeConfig, - getQueueGroupName, - getSubscriptionTopic, - getTopicName, - NatsBridge, - puristaVersion, + NatsBridge, + getCommandSubscriptionTopic, + getDefaultNatsBridgeConfig, + getQueueGroupName, + getSubscriptionTopic, + getTopicName, + puristaVersion, } from './index.js' describe('exports', () => { - it('has a version', () => { - expect(puristaVersion).toBeDefined() - }) + it('has a version', () => { + expect(puristaVersion).toBeDefined() + }) - it('exports NatsBridge', () => { - expect(NatsBridge).toBeDefined() - }) + it('exports NatsBridge', () => { + expect(NatsBridge).toBeDefined() + }) - it('exports getCommandSubscriptionTopic', () => { - expect(getCommandSubscriptionTopic).toBeDefined() - }) + it('exports getCommandSubscriptionTopic', () => { + expect(getCommandSubscriptionTopic).toBeDefined() + }) - it('exports getDefaultNatsBridgeConfig', () => { - expect(getDefaultNatsBridgeConfig).toBeDefined() - }) + it('exports getDefaultNatsBridgeConfig', () => { + expect(getDefaultNatsBridgeConfig).toBeDefined() + }) - it('exports getQueueGroupName', () => { - expect(getQueueGroupName).toBeDefined() - }) + it('exports getQueueGroupName', () => { + expect(getQueueGroupName).toBeDefined() + }) - it('exports getSubscriptionTopic', () => { - expect(getSubscriptionTopic).toBeDefined() - }) + it('exports getSubscriptionTopic', () => { + expect(getSubscriptionTopic).toBeDefined() + }) - it('exports getTopicName', () => { - expect(getTopicName).toBeDefined() - }) + it('exports getTopicName', () => { + expect(getTopicName).toBeDefined() + }) }) diff --git a/packages/natsbridge/src/serializeOtpToNats.impl.ts b/packages/natsbridge/src/serializeOtpToNats.impl.ts index 8fd703e34..e6449a2a8 100644 --- a/packages/natsbridge/src/serializeOtpToNats.impl.ts +++ b/packages/natsbridge/src/serializeOtpToNats.impl.ts @@ -1,6 +1,6 @@ import { context, propagation } from '@opentelemetry/api' export const serializeOtpToNats = function >(serializedContext: T) { - propagation.inject(context.active(), serializedContext) - return serializedContext + propagation.inject(context.active(), serializedContext) + return serializedContext } diff --git a/packages/natsbridge/src/topic/getCommandSubscriptionTopic.impl.ts b/packages/natsbridge/src/topic/getCommandSubscriptionTopic.impl.ts index f579636f8..1f594a505 100644 --- a/packages/natsbridge/src/topic/getCommandSubscriptionTopic.impl.ts +++ b/packages/natsbridge/src/topic/getCommandSubscriptionTopic.impl.ts @@ -1,24 +1,24 @@ import type { EBMessageAddress } from '@purista/core' -import { convertToSnakeCase, EBMessageType } from '@purista/core' +import { EBMessageType, convertToSnakeCase } from '@purista/core' import type { NatsBridge } from '../NatsBridge.js' type GetCommandTopicFn = (this: NatsBridge, address: EBMessageAddress) => string export const getCommandSubscriptionTopic: GetCommandTopicFn = function (address) { - return [ - this.config.topicPrefix, - convertToSnakeCase(EBMessageType.Command), - '*', - '*', - '*', - '*', - '*', - '*', - '*', - '*', - convertToSnakeCase(address.serviceName), - convertToSnakeCase(address.serviceVersion), - convertToSnakeCase(address.serviceTarget), - ].join('.') + return [ + this.config.topicPrefix, + convertToSnakeCase(EBMessageType.Command), + '*', + '*', + '*', + '*', + '*', + '*', + '*', + '*', + convertToSnakeCase(address.serviceName), + convertToSnakeCase(address.serviceVersion), + convertToSnakeCase(address.serviceTarget), + ].join('.') } diff --git a/packages/natsbridge/src/topic/getCommandSubscriptionTopic.test.ts b/packages/natsbridge/src/topic/getCommandSubscriptionTopic.test.ts index 2b6f5529d..34314d2ec 100644 --- a/packages/natsbridge/src/topic/getCommandSubscriptionTopic.test.ts +++ b/packages/natsbridge/src/topic/getCommandSubscriptionTopic.test.ts @@ -1,27 +1,27 @@ import { getLoggerMock, safeBind } from '@purista/core' -import { getDefaultNatsBridgeConfig } from '../getDefaultNatsBridgeConfig.js' import type { NatsBridge } from '../NatsBridge.js' +import { getDefaultNatsBridgeConfig } from '../getDefaultNatsBridgeConfig.js' import { getCommandSubscriptionTopic } from './getCommandSubscriptionTopic.impl.js' describe('getCommandSubscriptionTopic', () => { - it('returns the command topic', () => { - const bridge = { - logger: getLoggerMock().mock, - config: { - ...getDefaultNatsBridgeConfig(), - }, - } as any as NatsBridge + it('returns the command topic', () => { + const bridge = { + logger: getLoggerMock().mock, + config: { + ...getDefaultNatsBridgeConfig(), + }, + } as any as NatsBridge - const topic = safeBind( - getCommandSubscriptionTopic, - bridge, - )({ - serviceName: 'testService', - serviceVersion: '1', - serviceTarget: 'testCommand', - }) + const topic = safeBind( + getCommandSubscriptionTopic, + bridge, + )({ + serviceName: 'testService', + serviceVersion: '1', + serviceTarget: 'testCommand', + }) - expect(topic).toBe('purista.command.*.*.*.*.*.*.*.*.test_service.1.test_command') - }) + expect(topic).toBe('purista.command.*.*.*.*.*.*.*.*.test_service.1.test_command') + }) }) diff --git a/packages/natsbridge/src/topic/getSubscriptionTopic.impl.ts b/packages/natsbridge/src/topic/getSubscriptionTopic.impl.ts index a36a12985..5253b269b 100644 --- a/packages/natsbridge/src/topic/getSubscriptionTopic.impl.ts +++ b/packages/natsbridge/src/topic/getSubscriptionTopic.impl.ts @@ -6,19 +6,19 @@ import type { NatsBridge } from '../NatsBridge.js' type GetSubscriptionTopicFn = (this: NatsBridge, subscription: Subscription) => string export const getSubscriptionTopic: GetSubscriptionTopicFn = function (subscription) { - return [ - this.config.topicPrefix, - convertToSnakeCase(subscription.messageType ?? '*'), - convertToSnakeCase(subscription.principalId ?? '*'), - convertToSnakeCase(subscription.tenantId ?? '*'), - convertToSnakeCase(subscription.sender?.instanceId ?? '*'), - convertToSnakeCase(subscription.sender?.serviceName ?? '*'), - convertToSnakeCase(subscription.sender?.serviceVersion ?? '*'), - convertToSnakeCase(subscription.sender?.serviceTarget ?? '*'), - convertToSnakeCase(subscription.eventName ?? '*'), - convertToSnakeCase(subscription.receiver?.instanceId ?? '*'), - convertToSnakeCase(subscription.receiver?.serviceName ?? '*'), - convertToSnakeCase(subscription.receiver?.serviceVersion ?? '*'), - convertToSnakeCase(subscription.receiver?.serviceTarget ?? '*'), - ].join('.') + return [ + this.config.topicPrefix, + convertToSnakeCase(subscription.messageType ?? '*'), + convertToSnakeCase(subscription.principalId ?? '*'), + convertToSnakeCase(subscription.tenantId ?? '*'), + convertToSnakeCase(subscription.sender?.instanceId ?? '*'), + convertToSnakeCase(subscription.sender?.serviceName ?? '*'), + convertToSnakeCase(subscription.sender?.serviceVersion ?? '*'), + convertToSnakeCase(subscription.sender?.serviceTarget ?? '*'), + convertToSnakeCase(subscription.eventName ?? '*'), + convertToSnakeCase(subscription.receiver?.instanceId ?? '*'), + convertToSnakeCase(subscription.receiver?.serviceName ?? '*'), + convertToSnakeCase(subscription.receiver?.serviceVersion ?? '*'), + convertToSnakeCase(subscription.receiver?.serviceTarget ?? '*'), + ].join('.') } diff --git a/packages/natsbridge/src/topic/getTopicName.impl.ts b/packages/natsbridge/src/topic/getTopicName.impl.ts index 0dae9fe26..18dede4e0 100644 --- a/packages/natsbridge/src/topic/getTopicName.impl.ts +++ b/packages/natsbridge/src/topic/getTopicName.impl.ts @@ -16,21 +16,21 @@ type GetTopicNameFn = (this: NatsBridge, message: EBMessage) => string * */ export const getTopicName: GetTopicNameFn = function (message: EBMessage) { - const empty = this.config.emptyTopicPartString + const empty = this.config.emptyTopicPartString - return [ - this.config.topicPrefix, - convertToSnakeCase(message.messageType), - convertToSnakeCase(message.principalId ?? empty), - convertToSnakeCase(message.tenantId ?? empty), - convertToSnakeCase(message.sender.instanceId ?? empty), - convertToSnakeCase(message.sender.serviceName), - convertToSnakeCase(message.sender.serviceVersion), - convertToSnakeCase(message.sender.serviceTarget), - convertToSnakeCase(message.eventName ?? empty), - convertToSnakeCase((message as Command).receiver?.instanceId ?? empty), - convertToSnakeCase((message as Command).receiver?.serviceName ?? empty), - convertToSnakeCase((message as Command).receiver?.serviceVersion ?? empty), - convertToSnakeCase((message as Command).receiver?.serviceTarget ?? empty), - ].join('.') + return [ + this.config.topicPrefix, + convertToSnakeCase(message.messageType), + convertToSnakeCase(message.principalId ?? empty), + convertToSnakeCase(message.tenantId ?? empty), + convertToSnakeCase(message.sender.instanceId ?? empty), + convertToSnakeCase(message.sender.serviceName), + convertToSnakeCase(message.sender.serviceVersion), + convertToSnakeCase(message.sender.serviceTarget), + convertToSnakeCase(message.eventName ?? empty), + convertToSnakeCase((message as Command).receiver?.instanceId ?? empty), + convertToSnakeCase((message as Command).receiver?.serviceName ?? empty), + convertToSnakeCase((message as Command).receiver?.serviceVersion ?? empty), + convertToSnakeCase((message as Command).receiver?.serviceTarget ?? empty), + ].join('.') } diff --git a/packages/natsbridge/src/types/NatsBridgeConfig.ts b/packages/natsbridge/src/types/NatsBridgeConfig.ts index 878139a33..2eb0f445b 100644 --- a/packages/natsbridge/src/types/NatsBridgeConfig.ts +++ b/packages/natsbridge/src/types/NatsBridgeConfig.ts @@ -4,51 +4,51 @@ import type { ConnectionOptions } from 'nats' * the configuration for the NATS event bridge */ export type NatsBridgeConfig = Prettify< - { - /** - * the prefix for topic to prevent name collisions - * - * @default purista - */ - topicPrefix: string + { + /** + * the prefix for topic to prevent name collisions + * + * @default purista + */ + topicPrefix: string - /** - * The string which should be used in topics for parts, which are undefined - * - * @default __none__ - */ - emptyTopicPartString: string + /** + * The string which should be used in topics for parts, which are undefined + * + * @default __none__ + */ + emptyTopicPartString: string - /** - * Indicates if a command response should be published a second time. - * If the command response gets published, it will be published to the regular topic pattern. - * - * If set to `never`, subscription might not get messages they are expecting because of the timing. - * - * If set to `always`, every command response is published. - * Because there might not be a consumer for every message, the broker will store the messages until the `defaultMessageExpiryInterval` is reached. - * This might result in a high ressource consumption of the broker. - * - * If set to `eventOnly`, only success responses which have a event name set, are published twice. - * There, we expect, that an event has at least one consumer subscription and the broker does not unnecessarily stores messages for a long time. - * - * @default eventOnly - */ - commandResponsePublishTwice: 'always' | 'eventOnly' | 'eventAndError' | 'never' + /** + * Indicates if a command response should be published a second time. + * If the command response gets published, it will be published to the regular topic pattern. + * + * If set to `never`, subscription might not get messages they are expecting because of the timing. + * + * If set to `always`, every command response is published. + * Because there might not be a consumer for every message, the broker will store the messages until the `defaultMessageExpiryInterval` is reached. + * This might result in a high resource consumption of the broker. + * + * If set to `eventOnly`, only success responses which have a event name set, are published twice. + * There, we expect, that an event has at least one consumer subscription and the broker does not unnecessarily stores messages for a long time. + * + * @default eventOnly + */ + commandResponsePublishTwice: 'always' | 'eventOnly' | 'eventAndError' | 'never' - /** - * the message expiry interval in seconds - * - * @default 30 days in seconds - */ - defaultMessageExpiryInterval: number + /** + * the message expiry interval in seconds + * + * @default 30 days in seconds + */ + defaultMessageExpiryInterval: number - /** - * maximum messages to run in parallel per subscription - * 10 means, each subscription can handle 10 calls at the same time - * - * @default 10 - */ - maxMessages: number - } & ConnectionOptions + /** + * maximum messages to run in parallel per subscription + * 10 means, each subscription can handle 10 calls at the same time + * + * @default 10 + */ + maxMessages: number + } & ConnectionOptions > diff --git a/packages/natsbridge/test/integration.test.ts b/packages/natsbridge/test/integration.test.ts index d463ec464..3b5ed603e 100644 --- a/packages/natsbridge/test/integration.test.ts +++ b/packages/natsbridge/test/integration.test.ts @@ -11,86 +11,78 @@ import { NatsBridge } from '../src/index.js' const EXAMPLE_EVENT = 'exampleEvent' describe('@purista/natsbridge', () => { - let container: StartedNatsContainer - let eventbridge: NatsBridge - const sandbox = createSandbox() - const subscriptionStub = sandbox.stub().resolves() - const logger = getLoggerMock(sandbox) - let service: Service - - beforeAll(async () => { - container = await new NatsContainer('nats:alpine') - // .withArg('-js', '-js') - .withLogConsumer((_stream) => { - // eslint-disable-next-line no-console - // stream.on('data', (line) => console.debug(line)) - // eslint-disable-next-line no-console - // stream.on('err', (line) => console.error('Error in NATS container', line)) - }) - .start() - - eventbridge = new NatsBridge({ logger: logger.mock, ...container.getConnectionOptions() }) - await eventbridge.start() - - const subscriptionBuilder = theServiceV1Service - .getSubscriptionBuilder('sendWelcomeEmail', 'send a welcome mail to new registered users') - .subscribeToEvent(EXAMPLE_EVENT) - .addPayloadSchema(z.any()) - .setSubscriptionFunction(subscriptionStub) - - theServiceServiceBuilder.addSubscriptionDefinition(subscriptionBuilder.getDefinition()) - - service = await theServiceServiceBuilder.getInstance(eventbridge, { logger: getLoggerMock(sandbox).mock }) - await service.start() - }) - - afterAll(async () => { - await service.destroy() - await eventbridge.destroy() - await container.stop() - }) - - afterEach(() => { - sandbox.resetHistory() - }) - - it('can invoke ping command', async () => { - const command = getCommandMessageMock({ - receiver: { - serviceName: service.info.serviceName, - serviceVersion: service.info.serviceVersion, - serviceTarget: 'ping', - }, - sender: { - serviceName: service.info.serviceName, - serviceVersion: service.info.serviceVersion, - serviceTarget: 'some', - instanceId: eventbridge.instanceId, - }, - payload: { - payload: undefined, - parameter: { - required: 'yes', - }, - }, - }) - const result = await eventbridge.invoke(command) - - expect(result).toEqual({ - ping: true, - }) - - expect(true).toBeTruthy() - }) - - it('receives subscriptions', async () => { - const payload = { example: 'payload' } - const commandResponse = getCommandSuccessMessageMock(payload, { eventName: EXAMPLE_EVENT }) - - await eventbridge.emitMessage(commandResponse) - - await new Promise((resolve) => setTimeout(resolve, 3000)) - - expect(subscriptionStub.called).toBeTruthy() - }) + let container: StartedNatsContainer + let eventbridge: NatsBridge + const sandbox = createSandbox() + const subscriptionStub = sandbox.stub().resolves() + const logger = getLoggerMock(sandbox) + let service: Service + + beforeAll(async () => { + container = await new NatsContainer('nats:alpine').start() + + eventbridge = new NatsBridge({ logger: logger.mock, ...container.getConnectionOptions() }) + await eventbridge.start() + + const subscriptionBuilder = theServiceV1Service + .getSubscriptionBuilder('sendWelcomeEmail', 'send a welcome mail to new registered users') + .subscribeToEvent(EXAMPLE_EVENT) + .addPayloadSchema(z.any()) + .setSubscriptionFunction(subscriptionStub) + + theServiceServiceBuilder.addSubscriptionDefinition(subscriptionBuilder.getDefinition()) + + service = await theServiceServiceBuilder.getInstance(eventbridge, { logger: getLoggerMock(sandbox).mock }) + await service.start() + }) + + afterAll(async () => { + await service.destroy() + await eventbridge.destroy() + await container.stop() + }) + + afterEach(() => { + sandbox.resetHistory() + }) + + it('can invoke ping command', async () => { + const command = getCommandMessageMock({ + receiver: { + serviceName: service.info.serviceName, + serviceVersion: service.info.serviceVersion, + serviceTarget: 'ping', + }, + sender: { + serviceName: service.info.serviceName, + serviceVersion: service.info.serviceVersion, + serviceTarget: 'some', + instanceId: eventbridge.instanceId, + }, + payload: { + payload: undefined, + parameter: { + required: 'yes', + }, + }, + }) + const result = await eventbridge.invoke(command) + + expect(result).toEqual({ + ping: true, + }) + + expect(true).toBeTruthy() + }) + + it('receives subscriptions', async () => { + const payload = { example: 'payload' } + const commandResponse = getCommandSuccessMessageMock(payload, { eventName: EXAMPLE_EVENT }) + + await eventbridge.emitMessage(commandResponse) + + await new Promise(resolve => setTimeout(resolve, 3000)) + + expect(subscriptionStub.called).toBeTruthy() + }) }) diff --git a/packages/natsbridge/tsconfig.json b/packages/natsbridge/tsconfig.json index b6433a134..8df85631b 100644 --- a/packages/natsbridge/tsconfig.json +++ b/packages/natsbridge/tsconfig.json @@ -1,21 +1,13 @@ { - "extends": "../../tsconfig.json", - "compilerOptions": { - "strictNullChecks": true, - "outDir": "./dist", - "declaration": true, - "sourceMap": false, - "declarationMap": true, - "types": [ - "vitest/globals", - "node" - ] - }, - "include": [ - "./src/**/*", - "./test/*", - ], - "exclude": [ - "./**/*.d.ts" - ] -} \ No newline at end of file + "extends": "../../tsconfig.json", + "compilerOptions": { + "strictNullChecks": true, + "outDir": "./dist", + "declaration": true, + "sourceMap": false, + "declarationMap": true, + "types": ["vitest/globals", "node"] + }, + "include": ["./src/**/*", "./test/*"], + "exclude": ["./**/*.d.ts"] +} diff --git a/packages/natsbridge/typedoc.json b/packages/natsbridge/typedoc.json index 355bf0f98..71c4b2283 100644 --- a/packages/natsbridge/typedoc.json +++ b/packages/natsbridge/typedoc.json @@ -1,6 +1,5 @@ { - - "extends": ["../../typedoc.base.json"], - "entryPoints": ["src/index.ts"], - "tsconfig": "./tsconfig.json" -} \ No newline at end of file + "extends": ["../../typedoc.base.json"], + "entryPoints": ["src/index.ts"], + "tsconfig": "./tsconfig.json" +} diff --git a/packages/redis-config-store/jsr.json b/packages/redis-config-store/jsr.json new file mode 100644 index 000000000..f35f3fd83 --- /dev/null +++ b/packages/redis-config-store/jsr.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://jsr.io/schema/config-file.v1.json", + "name": "@purista/redis-config-store", + "version": "1.11.0", + "description": "State store with redis as database", + "keywords": ["purista", "redis", "typescript", "javascript"], + "exports": "./dist/esm/index.js", + "publish": { + "include": ["dist/**/*.js", "dist/**/*.d.ts", "README.md", "package.json"], + "exclude": [ + "src", + ".github", + ".vscode", + ".zed", + "!dist", + "!dist/**/*.js", + "!dist/**/*.d.ts", + ".tshy", + ".tshy-build", + "vendor", + "docs", + "typedoc.json", + "..eslintcache", + ".npmignore" + ] + } +} diff --git a/packages/redis-config-store/package.json b/packages/redis-config-store/package.json index a6fd3ac26..bac3cad12 100644 --- a/packages/redis-config-store/package.json +++ b/packages/redis-config-store/package.json @@ -1,63 +1,60 @@ { - "name": "@purista/redis-config-store", - "version": "1.11.0", - "description": "State store with redis as database", - "homepage": "https://purista.dev", - "repository": { - "type": "git", - "url": "git@github.com:sebastianwessel/purista.git" - }, - "author": "Sebastian Wessel", - "license": "ISC", - "type": "module", - "main": "./dist/commonjs/index.js", - "exports": { - "./package.json": "./package.json", - ".": { - "import": { - "types": "./dist/esm/index.d.ts", - "default": "./dist/esm/index.js" - }, - "require": { - "types": "./dist/commonjs/index.d.ts", - "default": "./dist/commonjs/index.js" - } - } - }, - "files": [ - "dist/**/*" - ], - "publishConfig": { - "access": "public" - }, - "engines": { - "node": ">=18.15" - }, - "scripts": { - "lint": "eslint . --ext .ts,.json --cache . --fix", - "test": "vitest", - "build": "rimraf dist && tshy" - }, - "tshy": { - "exclude": [ - "src/**/*.test.ts" - ], - "exports": { - "./package.json": "./package.json", - ".": "./src/index.ts" - } - }, - "devDependencies": { - "@types/node": "^20.11.17", - "@types/sinon": "^17.0.3", - "sinon": "^17.0.1", - "tshy": "^1.11.1", - "vitest": "^1.3.0" - }, - "dependencies": { - "@purista/core": "*", - "@redis/client": "^1.5.13" - }, - "peerDependenciesMeta": {}, - "types": "./dist/commonjs/index.d.ts" + "name": "@purista/redis-config-store", + "version": "1.11.0", + "description": "State store with redis as database", + "homepage": "https://purista.dev", + "repository": { + "type": "git", + "url": "git@github.com:puristajs/purista.git" + }, + "author": "Sebastian Wessel", + "license": "ISC", + "type": "module", + "main": "./dist/commonjs/index.js", + "exports": { + "./package.json": "./package.json", + ".": { + "import": { + "types": "./dist/esm/index.d.ts", + "default": "./dist/esm/index.js" + }, + "require": { + "types": "./dist/commonjs/index.d.ts", + "default": "./dist/commonjs/index.js" + } + } + }, + "files": ["dist/**/*"], + "publishConfig": { + "access": "public" + }, + "engines": { + "node": ">=18.15" + }, + "scripts": { + "lint": "npx @biomejs/biome check --write", + "test": "vitest", + "build": "rimraf dist && tshy" + }, + "tshy": { + "exclude": ["src/**/*.test.ts"], + "exports": { + "./package.json": "./package.json", + ".": "./src/index.ts" + } + }, + "devDependencies": { + "@types/node": "^22.5.1", + "@types/sinon": "^17.0.3", + "sinon": "^19.0.2", + "tshy": "^3.0.2", + "vitest": "^3.0.4" + }, + "dependencies": { + "@purista/core": "*", + "@redis/client": "^1.5.16" + }, + "peerDependenciesMeta": {}, + "types": "./dist/commonjs/index.d.ts", + "module": "./dist/esm/index.js" } diff --git a/packages/redis-config-store/src/RedisConfigStore.impl.ts b/packages/redis-config-store/src/RedisConfigStore.impl.ts index 2c818d6ca..8371180c9 100644 --- a/packages/redis-config-store/src/RedisConfigStore.impl.ts +++ b/packages/redis-config-store/src/RedisConfigStore.impl.ts @@ -39,70 +39,70 @@ import type { RedisStoreConfig } from './types.js' * */ export class RedisConfigStore< - M extends RedisModules = RedisModules, - F extends RedisFunctions = RedisFunctions, - S extends RedisScripts = RedisScripts, + M extends RedisModules = RedisModules, + F extends RedisFunctions = RedisFunctions, + S extends RedisScripts = RedisScripts, > extends ConfigStoreBaseClass> { - public client: RedisClientType + public client: RedisClientType - constructor(config?: StoreBaseConfig>) { - super('RedisConfigStore', { ...config }) - this.client = createClient(this.config.config) - this.client.on('error', (err) => this.logger.error({ err }, 'Redis Client Error')) - } + constructor(config?: StoreBaseConfig>) { + super('RedisConfigStore', { ...config }) + this.client = createClient(this.config.config) + this.client.on('error', err => this.logger.error({ err }, 'Redis Client Error')) + } - protected async getClient() { - if (this.client.isOpen) { - return this.client - } - return this.client.connect() - } + protected async getClient() { + if (this.client.isOpen) { + return this.client + } + return this.client.connect() + } - protected async getConfigImpl( - ...configNames: ConfigNames - ): Promise> { - const client = await this.getClient() + protected async getConfigImpl( + ...configNames: ConfigNames + ): Promise> { + const client = await this.getClient() - const result: Record = {} - for await (const name of configNames) { - try { - const value = await client.get(name) - result[name] = value ? JSON.parse(value) : undefined - } catch (err) { - const msg = `error in config store getting value ${name}` - this.logger.error({ err }, msg) - throw new UnhandledError(StatusCode.InternalServerError, msg) - } - } - return result as ObjectWithKeysFromStringArray - } + const result: Record = {} + for await (const name of configNames) { + try { + const value = await client.get(name) + result[name] = value ? JSON.parse(value) : undefined + } catch (err) { + const msg = `error in config store getting value ${name}` + this.logger.error({ err }, msg) + throw new UnhandledError(StatusCode.InternalServerError, msg) + } + } + return result as ObjectWithKeysFromStringArray + } - protected async removeConfigImpl(configName: string) { - const client = await this.getClient() + protected async removeConfigImpl(configName: string) { + const client = await this.getClient() - try { - await client.del(configName) - } catch (err) { - const msg = `error in config store removing value ${configName}` - this.logger.error({ err }, msg) - throw new UnhandledError(StatusCode.InternalServerError, msg) - } - } + try { + await client.del(configName) + } catch (err) { + const msg = `error in config store removing value ${configName}` + this.logger.error({ err }, msg) + throw new UnhandledError(StatusCode.InternalServerError, msg) + } + } - protected async setConfigImpl(configName: string, configValue: unknown) { - const client = await this.getClient() - try { - await client.set(configName, JSON.stringify(configValue)) - } catch (err) { - const msg = `error in config store setting value ${configName}` - this.logger.error({ err }, msg) - throw new UnhandledError(StatusCode.InternalServerError, msg) - } - } + protected async setConfigImpl(configName: string, configValue: unknown) { + const client = await this.getClient() + try { + await client.set(configName, JSON.stringify(configValue)) + } catch (err) { + const msg = `error in config store setting value ${configName}` + this.logger.error({ err }, msg) + throw new UnhandledError(StatusCode.InternalServerError, msg) + } + } - async destroy() { - if (this.client.isOpen) { - await this.client.disconnect() - } - } + async destroy() { + if (this.client.isOpen) { + await this.client.disconnect() + } + } } diff --git a/packages/redis-config-store/src/index.test.ts b/packages/redis-config-store/src/index.test.ts index 71364ea10..4265a345f 100644 --- a/packages/redis-config-store/src/index.test.ts +++ b/packages/redis-config-store/src/index.test.ts @@ -1,11 +1,11 @@ -import { puristaVersion, RedisConfigStore } from './index.js' +import { RedisConfigStore, puristaVersion } from './index.js' describe('exports redis-config-store', () => { - it('has a version', () => { - expect(puristaVersion).toBeDefined() - }) + it('has a version', () => { + expect(puristaVersion).toBeDefined() + }) - it('exports RedisConfigStore', () => { - expect(RedisConfigStore).toBeDefined() - }) + it('exports RedisConfigStore', () => { + expect(RedisConfigStore).toBeDefined() + }) }) diff --git a/packages/redis-config-store/src/types.ts b/packages/redis-config-store/src/types.ts index 2ebb4d91d..49c240f64 100644 --- a/packages/redis-config-store/src/types.ts +++ b/packages/redis-config-store/src/types.ts @@ -5,9 +5,9 @@ import type { RedisClientOptions, RedisFunctions, RedisModules, RedisScripts } f * It will extend the StoreBaseConfig. */ export type RedisStoreConfig< - M extends RedisModules = RedisModules, - F extends RedisFunctions = RedisFunctions, - S extends RedisScripts = RedisScripts, + M extends RedisModules = RedisModules, + F extends RedisFunctions = RedisFunctions, + S extends RedisScripts = RedisScripts, > = { - config?: RedisClientOptions + config?: RedisClientOptions } diff --git a/packages/redis-config-store/test/integration.test.ts b/packages/redis-config-store/test/integration.test.ts index a8938b0a3..5b4b34ace 100644 --- a/packages/redis-config-store/test/integration.test.ts +++ b/packages/redis-config-store/test/integration.test.ts @@ -7,68 +7,63 @@ import { RedisConfigStore } from '../src/RedisConfigStore.impl.js' const REDIS_PORT = 6379 describe('@purista/redis-state-store', () => { - let container: StartedTestContainer + let container: StartedTestContainer - beforeAll(async () => { - container = await new GenericContainer('redis') - .withExposedPorts({ - container: REDIS_PORT, - host: REDIS_PORT - 1, - }) - .withWaitStrategy(Wait.forLogMessage('Ready to accept connections')) - .withLogConsumer((_stream) => { - // stream.on('data', (line) => console.debug(line)) - // eslint-disable-next-line no-console - // stream.on('err', (line) => console.error(line)) - }) - .start() - }) + beforeAll(async () => { + container = await new GenericContainer('redis') + .withExposedPorts({ + container: REDIS_PORT, + host: REDIS_PORT - 1, + }) + .withWaitStrategy(Wait.forLogMessage('Ready to accept connections')) + .start() + }) - afterAll(async () => { - await container.stop() - }) + afterAll(async () => { + await container.stop() + }) - it('set, get and remove values', async () => { - const config = { - url: `redis://127.0.0.1:${REDIS_PORT - 1}`, - } + it('set, get and remove values', async () => { + const config = { + url: `redis://127.0.0.1:${REDIS_PORT - 1}`, + } - const store = new RedisConfigStore({ config, enableSet: true, enableRemove: true, logger: getLoggerMock().mock }) + const store = new RedisConfigStore({ config, enableSet: true, enableRemove: true, logger: getLoggerMock().mock }) - await expect(store.setConfig('myConfig', { some: 'value' })).resolves.toBeUndefined() + await expect(store.setConfig('myConfig', { some: 'value' })).resolves.toBeUndefined() - const value = await store.getConfig('myConfig') - expect(value).toStrictEqual({ - myConfig: { some: 'value' }, - }) + const value = await store.getConfig('myConfig') + expect(value).toStrictEqual({ + myConfig: { some: 'value' }, + }) - await expect(store.removeConfig('myConfig')).resolves.toBeUndefined() - await expect(store.getConfig('myConfig')).resolves.toStrictEqual({ - myConfig: undefined, - }) + await expect(store.removeConfig('myConfig')).resolves.toBeUndefined() + await expect(store.getConfig('myConfig')).resolves.toStrictEqual({ + myConfig: undefined, + }) - await expect(store.destroy()).resolves.toBeUndefined() - }) + await expect(store.destroy()).resolves.toBeUndefined() + }) - it('throws on disabled features', async () => { - const config = { - url: `redis://127.0.0.1:${REDIS_PORT - 1}`, - } + it('throws on disabled features', async () => { + const config = { + url: `redis://127.0.0.1:${REDIS_PORT - 1}`, + } - const store = new RedisConfigStore({ - enableGet: false, - enableRemove: false, - enableSet: false, - config, - logger: getLoggerMock().mock, - }) + const store = new RedisConfigStore({ + enableGet: false, + enableRemove: false, + enableSet: false, + config, + logger: getLoggerMock().mock, + }) - await expect(store.setConfig('myConfig', { some: 'value' })).rejects.toThrow( - 'set config at store is disabled by config', - ) - await expect(store.getConfig('myConfig')).rejects.toThrow('get config from store is disabled by config') - await expect(store.removeConfig('myConfig')).rejects.toThrow('remove config from store is disabled by config') + await expect(store.setConfig('myConfig', { some: 'value' })).rejects.toThrow( + 'set config at store is disabled by config', + ) + await expect(store.getConfig('myConfig')).rejects.toThrow('get config from store is disabled by config') + await expect(store.removeConfig('myConfig')).rejects.toThrow('remove config from store is disabled by config') - await expect(store.destroy()).resolves.toBeUndefined() - }) + await expect(store.destroy()).resolves.toBeUndefined() + }) }) diff --git a/packages/redis-config-store/tsconfig.json b/packages/redis-config-store/tsconfig.json index 1d4563023..57537600c 100644 --- a/packages/redis-config-store/tsconfig.json +++ b/packages/redis-config-store/tsconfig.json @@ -1,22 +1,14 @@ { - "extends": "../../tsconfig.json", - "compilerOptions": { - "strictNullChecks": true, - "outDir": "./dist", - "declaration": true, - "sourceMap": false, - "declarationMap": true, - "types": [ - "vitest/globals", - "node" - ] - }, - "exclude": [ - "./**/*.d.ts" - ], - - "include": [ - "./src/**/*", - "./test/*", - ], -} \ No newline at end of file + "extends": "../../tsconfig.json", + "compilerOptions": { + "strictNullChecks": true, + "outDir": "./dist", + "declaration": true, + "sourceMap": false, + "declarationMap": true, + "types": ["vitest/globals", "node"] + }, + "exclude": ["./**/*.d.ts"], + + "include": ["./src/**/*", "./test/*"] +} diff --git a/packages/redis-config-store/typedoc.json b/packages/redis-config-store/typedoc.json index 355bf0f98..71c4b2283 100644 --- a/packages/redis-config-store/typedoc.json +++ b/packages/redis-config-store/typedoc.json @@ -1,6 +1,5 @@ { - - "extends": ["../../typedoc.base.json"], - "entryPoints": ["src/index.ts"], - "tsconfig": "./tsconfig.json" -} \ No newline at end of file + "extends": ["../../typedoc.base.json"], + "entryPoints": ["src/index.ts"], + "tsconfig": "./tsconfig.json" +} diff --git a/packages/redis-state-store/jsr.json b/packages/redis-state-store/jsr.json new file mode 100644 index 000000000..9c5e96f3e --- /dev/null +++ b/packages/redis-state-store/jsr.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://jsr.io/schema/config-file.v1.json", + "name": "@purista/redis-state-store", + "version": "1.11.0", + "description": "State store with redis as database", + "keywords": ["purista", "redis", "typescript", "javascript"], + "exports": "./dist/esm/index.js", + "publish": { + "include": ["dist/**/*.js", "dist/**/*.d.ts", "README.md", "package.json"], + "exclude": [ + "src", + ".github", + ".vscode", + ".zed", + "!dist", + "!dist/**/*.js", + "!dist/**/*.d.ts", + ".tshy", + ".tshy-build", + "vendor", + "docs", + "typedoc.json", + "..eslintcache", + ".npmignore" + ] + } +} diff --git a/packages/redis-state-store/package.json b/packages/redis-state-store/package.json index 4377d6f15..8f9dc3405 100644 --- a/packages/redis-state-store/package.json +++ b/packages/redis-state-store/package.json @@ -1,63 +1,60 @@ { - "name": "@purista/redis-state-store", - "version": "1.11.0", - "description": "State store with redis as database", - "homepage": "https://purista.dev", - "repository": { - "type": "git", - "url": "git@github.com:sebastianwessel/purista.git" - }, - "author": "Sebastian Wessel", - "license": "ISC", - "type": "module", - "main": "./dist/commonjs/index.js", - "exports": { - "./package.json": "./package.json", - ".": { - "import": { - "types": "./dist/esm/index.d.ts", - "default": "./dist/esm/index.js" - }, - "require": { - "types": "./dist/commonjs/index.d.ts", - "default": "./dist/commonjs/index.js" - } - } - }, - "files": [ - "dist/**/*" - ], - "publishConfig": { - "access": "public" - }, - "engines": { - "node": ">=18.15" - }, - "scripts": { - "lint": "eslint . --ext .ts,.json --cache . --fix", - "test": "vitest", - "build": "rimraf dist && tshy" - }, - "tshy": { - "exclude": [ - "src/**/*.test.ts" - ], - "exports": { - "./package.json": "./package.json", - ".": "./src/index.ts" - } - }, - "devDependencies": { - "@types/node": "^20.11.17", - "@types/sinon": "^17.0.3", - "sinon": "^17.0.1", - "tshy": "^1.11.1", - "vitest": "^1.3.0" - }, - "dependencies": { - "@purista/core": "*", - "@redis/client": "^1.5.13" - }, - "peerDependenciesMeta": {}, - "types": "./dist/commonjs/index.d.ts" + "name": "@purista/redis-state-store", + "version": "1.11.0", + "description": "State store with redis as database", + "homepage": "https://purista.dev", + "repository": { + "type": "git", + "url": "git@github.com:puristajs/purista.git" + }, + "author": "Sebastian Wessel", + "license": "ISC", + "type": "module", + "main": "./dist/commonjs/index.js", + "exports": { + "./package.json": "./package.json", + ".": { + "import": { + "types": "./dist/esm/index.d.ts", + "default": "./dist/esm/index.js" + }, + "require": { + "types": "./dist/commonjs/index.d.ts", + "default": "./dist/commonjs/index.js" + } + } + }, + "files": ["dist/**/*"], + "publishConfig": { + "access": "public" + }, + "engines": { + "node": ">=18.15" + }, + "scripts": { + "lint": "npx @biomejs/biome check --write", + "test": "vitest", + "build": "rimraf dist && tshy" + }, + "tshy": { + "exclude": ["src/**/*.test.ts"], + "exports": { + "./package.json": "./package.json", + ".": "./src/index.ts" + } + }, + "devDependencies": { + "@types/node": "^22.5.1", + "@types/sinon": "^17.0.3", + "sinon": "^19.0.2", + "tshy": "^3.0.2", + "vitest": "^3.0.4" + }, + "dependencies": { + "@purista/core": "*", + "@redis/client": "^1.5.16" + }, + "peerDependenciesMeta": {}, + "types": "./dist/commonjs/index.d.ts", + "module": "./dist/esm/index.js" } diff --git a/packages/redis-state-store/src/RedisStateStore.impl.ts b/packages/redis-state-store/src/RedisStateStore.impl.ts index 6e929aee7..c91f6b948 100644 --- a/packages/redis-state-store/src/RedisStateStore.impl.ts +++ b/packages/redis-state-store/src/RedisStateStore.impl.ts @@ -39,74 +39,74 @@ import type { RedisStoreConfig } from './types.js' * */ export class RedisStateStore< - M extends RedisModules = RedisModules, - F extends RedisFunctions = RedisFunctions, - S extends RedisScripts = RedisScripts, + M extends RedisModules = RedisModules, + F extends RedisFunctions = RedisFunctions, + S extends RedisScripts = RedisScripts, > extends StateStoreBaseClass> { - public client: RedisClientType + public client: RedisClientType - constructor(config?: StoreBaseConfig>) { - super('RedisStateStore', { ...config }) - this.client = createClient(this.config.config) - this.client.on('error', (err) => this.logger.error({ err }, 'Redis Client Error')) - } + constructor(config?: StoreBaseConfig>) { + super('RedisStateStore', { ...config }) + this.client = createClient(this.config.config) + this.client.on('error', err => this.logger.error({ err }, 'Redis Client Error')) + } - protected async getClient() { - if (this.client.isOpen) { - return this.client - } - return this.client.connect() - } + protected async getClient() { + if (this.client.isOpen) { + return this.client + } + return this.client.connect() + } - protected async getStateImpl( - ...stateNames: StateNames - ): Promise> { - const client = await this.getClient() + protected async getStateImpl( + ...stateNames: StateNames + ): Promise> { + const client = await this.getClient() - const result: Record = {} - for await (const name of stateNames) { - try { - const value = await client.get(name) - result[name] = value ? JSON.parse(value) : undefined - } catch (err) { - const msg = `error in state store getting value ${name}` - this.logger.error({ err }, msg) - throw new UnhandledError(StatusCode.InternalServerError, msg) - } - } - return result as ObjectWithKeysFromStringArray - } + const result: Record = {} + for await (const name of stateNames) { + try { + const value = await client.get(name) + result[name] = value ? JSON.parse(value) : undefined + } catch (err) { + const msg = `error in state store getting value ${name}` + this.logger.error({ err }, msg) + throw new UnhandledError(StatusCode.InternalServerError, msg) + } + } + return result as ObjectWithKeysFromStringArray + } - protected async removeStateImpl(stateName: string) { - const client = await this.getClient() + protected async removeStateImpl(stateName: string) { + const client = await this.getClient() - try { - await client.del(stateName) - } catch (err) { - const msg = `error in state store removing value ${stateName}` - this.logger.error({ err }, msg) - throw new UnhandledError(StatusCode.InternalServerError, msg) - } - } + try { + await client.del(stateName) + } catch (err) { + const msg = `error in state store removing value ${stateName}` + this.logger.error({ err }, msg) + throw new UnhandledError(StatusCode.InternalServerError, msg) + } + } - protected async setStateImpl(stateName: string, stateValue: unknown) { - if (!this.config.enableSet) { - throw new UnhandledError(StatusCode.Unauthorized, 'set state at store is disabled by config') - } + protected async setStateImpl(stateName: string, stateValue: unknown) { + if (!this.config.enableSet) { + throw new UnhandledError(StatusCode.Unauthorized, 'set state at store is disabled by config') + } - const client = await this.getClient() - try { - await client.set(stateName, JSON.stringify(stateValue)) - } catch (err) { - const msg = `error in state store setting value ${stateName}` - this.logger.error({ err }, msg) - throw new UnhandledError(StatusCode.InternalServerError, msg) - } - } + const client = await this.getClient() + try { + await client.set(stateName, JSON.stringify(stateValue)) + } catch (err) { + const msg = `error in state store setting value ${stateName}` + this.logger.error({ err }, msg) + throw new UnhandledError(StatusCode.InternalServerError, msg) + } + } - async destroy() { - if (this.client.isOpen) { - await this.client.disconnect() - } - } + async destroy() { + if (this.client.isOpen) { + await this.client.disconnect() + } + } } diff --git a/packages/redis-state-store/src/index.test.ts b/packages/redis-state-store/src/index.test.ts index 460834701..977010bed 100644 --- a/packages/redis-state-store/src/index.test.ts +++ b/packages/redis-state-store/src/index.test.ts @@ -1,11 +1,11 @@ -import { puristaVersion, RedisStateStore } from './index.js' +import { RedisStateStore, puristaVersion } from './index.js' describe('exports redis-state-store', () => { - it('has a version', () => { - expect(puristaVersion).toBeDefined() - }) + it('has a version', () => { + expect(puristaVersion).toBeDefined() + }) - it('exports RedisStateStore', () => { - expect(RedisStateStore).toBeDefined() - }) + it('exports RedisStateStore', () => { + expect(RedisStateStore).toBeDefined() + }) }) diff --git a/packages/redis-state-store/src/types.ts b/packages/redis-state-store/src/types.ts index 2ebb4d91d..49c240f64 100644 --- a/packages/redis-state-store/src/types.ts +++ b/packages/redis-state-store/src/types.ts @@ -5,9 +5,9 @@ import type { RedisClientOptions, RedisFunctions, RedisModules, RedisScripts } f * It will extend the StoreBaseConfig. */ export type RedisStoreConfig< - M extends RedisModules = RedisModules, - F extends RedisFunctions = RedisFunctions, - S extends RedisScripts = RedisScripts, + M extends RedisModules = RedisModules, + F extends RedisFunctions = RedisFunctions, + S extends RedisScripts = RedisScripts, > = { - config?: RedisClientOptions + config?: RedisClientOptions } diff --git a/packages/redis-state-store/test/integration.test.ts b/packages/redis-state-store/test/integration.test.ts index f9ee19a96..c04207f57 100644 --- a/packages/redis-state-store/test/integration.test.ts +++ b/packages/redis-state-store/test/integration.test.ts @@ -6,62 +6,57 @@ import { RedisStateStore } from '../src/RedisStateStore.impl.js' const REDIS_PORT = 6379 describe('@purista/redis-state-store', () => { - let container: StartedTestContainer - - beforeAll(async () => { - container = await new GenericContainer('redis') - .withExposedPorts({ - container: REDIS_PORT, - host: REDIS_PORT, - }) - .withWaitStrategy(Wait.forLogMessage('Ready to accept connections')) - .withLogConsumer((_stream) => { - // stream.on('data', (line) => console.debug(line)) - // eslint-disable-next-line no-console - // stream.on('err', (line) => console.error(line)) - }) - .start() - }) - - afterAll(async () => { - await container.stop() - }) - - it('set, get and remove values', async () => { - const config = { - url: `redis://127.0.0.1:${REDIS_PORT}`, - } - - const store = new RedisStateStore({ config }) - - await expect(store.setState('myState', { some: 'value' })).resolves.toBeUndefined() - - const value = await store.getState('myState') - expect(value).toStrictEqual({ - myState: { some: 'value' }, - }) - - await expect(store.removeState('myState')).resolves.toBeUndefined() - await expect(store.getState('myState')).resolves.toStrictEqual({ - myState: undefined, - }) - - await expect(store.destroy()).resolves.toBeUndefined() - }) - - it('throws on disabled features', async () => { - const config = { - url: `redis://127.0.0.1:${REDIS_PORT}`, - } - - const store = new RedisStateStore({ enableGet: false, enableRemove: false, enableSet: false, config }) - - await expect(store.setState('myState', { some: 'value' })).rejects.toThrow( - 'set state at store is disabled by config', - ) - await expect(store.getState('myState')).rejects.toThrow('get state from store is disabled by config') - await expect(store.removeState('myState')).rejects.toThrow('remove state from store is disabled by config') - - await expect(store.destroy()).resolves.toBeUndefined() - }) + let container: StartedTestContainer + + beforeAll(async () => { + container = await new GenericContainer('redis') + .withExposedPorts({ + container: REDIS_PORT, + host: REDIS_PORT, + }) + .withWaitStrategy(Wait.forLogMessage('Ready to accept connections')) + .start() + }) + + afterAll(async () => { + await container.stop() + }) + + it('set, get and remove values', async () => { + const config = { + url: `redis://127.0.0.1:${REDIS_PORT}`, + } + + const store = new RedisStateStore({ config }) + + await expect(store.setState('myState', { some: 'value' })).resolves.toBeUndefined() + + const value = await store.getState('myState') + expect(value).toStrictEqual({ + myState: { some: 'value' }, + }) + + await expect(store.removeState('myState')).resolves.toBeUndefined() + await expect(store.getState('myState')).resolves.toStrictEqual({ + myState: undefined, + }) + + await expect(store.destroy()).resolves.toBeUndefined() + }) + + it('throws on disabled features', async () => { + const config = { + url: `redis://127.0.0.1:${REDIS_PORT}`, + } + + const store = new RedisStateStore({ enableGet: false, enableRemove: false, enableSet: false, config }) + + await expect(store.setState('myState', { some: 'value' })).rejects.toThrow( + 'set state at store is disabled by config', + ) + await expect(store.getState('myState')).rejects.toThrow('get state from store is disabled by config') + await expect(store.removeState('myState')).rejects.toThrow('remove state from store is disabled by config') + + await expect(store.destroy()).resolves.toBeUndefined() + }) }) diff --git a/packages/redis-state-store/tsconfig.json b/packages/redis-state-store/tsconfig.json index e2c5e049b..84443e71d 100644 --- a/packages/redis-state-store/tsconfig.json +++ b/packages/redis-state-store/tsconfig.json @@ -1,22 +1,14 @@ { - "extends": "../../tsconfig.json", - "compilerOptions": { - "outDir": "./dist", - "declaration": true, - "sourceMap": false, - "declarationMap": true, - "strictNullChecks": true, - "types": [ - "vitest/globals", - "node" - ] - }, - "exclude": [ - "./**/*.d.ts" - ], - - "include": [ - "./src/**/*", - "./test/*", - ], -} \ No newline at end of file + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "./dist", + "declaration": true, + "sourceMap": false, + "declarationMap": true, + "strictNullChecks": true, + "types": ["vitest/globals", "node"] + }, + "exclude": ["./**/*.d.ts"], + + "include": ["./src/**/*", "./test/*"] +} diff --git a/packages/redis-state-store/typedoc.json b/packages/redis-state-store/typedoc.json index 355bf0f98..71c4b2283 100644 --- a/packages/redis-state-store/typedoc.json +++ b/packages/redis-state-store/typedoc.json @@ -1,6 +1,5 @@ { - - "extends": ["../../typedoc.base.json"], - "entryPoints": ["src/index.ts"], - "tsconfig": "./tsconfig.json" -} \ No newline at end of file + "extends": ["../../typedoc.base.json"], + "entryPoints": ["src/index.ts"], + "tsconfig": "./tsconfig.json" +} diff --git a/scripts/commitVersion.sh b/scripts/commitVersion.sh index 97b8b3a1d..8264bd4f8 100755 --- a/scripts/commitVersion.sh +++ b/scripts/commitVersion.sh @@ -1,15 +1,41 @@ -NODE_VERSION=$(node -p -e "require('./package.json').version") -echo $NODE_VERSION +#!/bin/bash +# Extract the version from package.json +NODE_VERSION=$(node -p "require('./package.json').version") + +# Print the extracted version +echo "Version from package.json: $NODE_VERSION" + +# Create the content to be written to version.ts CONTENT="export const puristaVersion = '$NODE_VERSION'" -for dir in ./packages/*/ # list directories in the form "/tmp/dirname/" -do - dir=${dir%*/} # remove the trailing "/" - echo "${dir##*/}" # print everything after the final "/" - echo $CONTENT > ./packages/${dir##*/}/src/version.ts -done +# Iterate over each subdirectory in the packages directory +for dir in ./packages/*/; do + # Remove trailing slash + dir=${dir%*/} -#git add . -#git commit -am "chore: bump versions to $NODE_VERSION" -S -#git tag -a v$NODE_VERSION -m "v$NODE_VERSION" \ No newline at end of file + # Extract the directory name + dirname=${dir##*/} + + # Print the directory name + echo "Processing package: $dirname" + + # Write the content to version.ts in the src subdirectory of each package + echo "$CONTENT" > "./packages/$dirname/src/version.ts" + + # Check if jsr.json exists in the current directory + if [ -f "./packages/$dirname/jsr.json" ]; then + echo "Updating jsr.json in $dirname" + + # Update the version field in jsr.json + node -e " + const fs = require('fs'); + const path = './packages/$dirname/jsr.json'; + const data = JSON.parse(fs.readFileSync(path, 'utf8')); + data.version = '$NODE_VERSION'; + fs.writeFileSync(path, JSON.stringify(data, null, 2)); + " + else + echo "jsr.json not found in $dirname" + fi +done diff --git a/test/service/theService/generalTheServiceServiceInfo.ts b/test/service/theService/generalTheServiceServiceInfo.ts index 45737f772..d4dc4a13a 100644 --- a/test/service/theService/generalTheServiceServiceInfo.ts +++ b/test/service/theService/generalTheServiceServiceInfo.ts @@ -1,6 +1,6 @@ import type { ServiceInfoType } from '@purista/core' export const generalTheServiceServiceInfo: Omit = { - serviceName: 'TheService', - serviceDescription: 'a example service', + serviceName: 'TheService', + serviceDescription: 'a example service', } diff --git a/test/service/theService/v1/command/foo/fooCommandBuilder.ts b/test/service/theService/v1/command/foo/fooCommandBuilder.ts index 9dec2d527..0e78033a6 100644 --- a/test/service/theService/v1/command/foo/fooCommandBuilder.ts +++ b/test/service/theService/v1/command/foo/fooCommandBuilder.ts @@ -1,18 +1,18 @@ import { theServiceServiceBuilder } from '../../theServiceServiceBuilder.js' import { - theServiceV1FooInputParameterSchema, - theServiceV1FooInputPayloadSchema, - theServiceV1FooOutputPayloadSchema, + theServiceV1FooInputParameterSchema, + theServiceV1FooInputPayloadSchema, + theServiceV1FooOutputPayloadSchema, } from './schema.js' export const fooCommandBuilder = theServiceServiceBuilder - .getCommandBuilder('foo', 'provide a dummy command') - .addPayloadSchema(theServiceV1FooInputPayloadSchema) - .addParameterSchema(theServiceV1FooInputParameterSchema) - .addOutputSchema(theServiceV1FooOutputPayloadSchema) - .setCommandFunction(async function (_context, payload, parameter) { - return { - payload, - parameter, - } - }) + .getCommandBuilder('foo', 'provide a dummy command') + .addPayloadSchema(theServiceV1FooInputPayloadSchema) + .addParameterSchema(theServiceV1FooInputParameterSchema) + .addOutputSchema(theServiceV1FooOutputPayloadSchema) + .setCommandFunction(async function (_context, payload, parameter) { + return { + payload, + parameter, + } + }) diff --git a/test/service/theService/v1/command/foo/schema.ts b/test/service/theService/v1/command/foo/schema.ts index 63bcab8c5..28d3ac6ca 100644 --- a/test/service/theService/v1/command/foo/schema.ts +++ b/test/service/theService/v1/command/foo/schema.ts @@ -9,11 +9,11 @@ export const theServiceV1FooInputPayloadSchema = extendApi(z.any(), { title: 'pi // define the output payload export const theServiceV1FooOutputPayloadSchema = extendApi( - z.object({ - payload: z.any(), - parameter: z.any(), - }), - { - title: 'ping output payload schema', - }, + z.object({ + payload: z.any(), + parameter: z.any(), + }), + { + title: 'ping output payload schema', + }, ) diff --git a/test/service/theService/v1/command/foo/types.ts b/test/service/theService/v1/command/foo/types.ts index c2f1b32ad..1eaea2ca2 100644 --- a/test/service/theService/v1/command/foo/types.ts +++ b/test/service/theService/v1/command/foo/types.ts @@ -1,9 +1,9 @@ import type { z } from 'zod' import type { - theServiceV1FooInputParameterSchema, - theServiceV1FooInputPayloadSchema, - theServiceV1FooOutputPayloadSchema, + theServiceV1FooInputParameterSchema, + theServiceV1FooInputPayloadSchema, + theServiceV1FooOutputPayloadSchema, } from './schema.js' export type TheServiceV1FooInputParameter = z.input diff --git a/test/service/theService/v1/command/invokeFoo/invokeFooCommandBuilder.ts b/test/service/theService/v1/command/invokeFoo/invokeFooCommandBuilder.ts index f726985b9..6e4ff25cb 100644 --- a/test/service/theService/v1/command/invokeFoo/invokeFooCommandBuilder.ts +++ b/test/service/theService/v1/command/invokeFoo/invokeFooCommandBuilder.ts @@ -2,25 +2,25 @@ import { z } from 'zod' import { theServiceServiceBuilder } from '../../theServiceServiceBuilder.js' import { - theServiceV1InvokeFooInputParameterSchema, - theServiceV1InvokeFooInputPayloadSchema, - theServiceV1InvokeFooOutputPayloadSchema, + theServiceV1InvokeFooInputParameterSchema, + theServiceV1InvokeFooInputPayloadSchema, + theServiceV1InvokeFooOutputPayloadSchema, } from './schema.js' export const invokeFooCommandBuilder = theServiceServiceBuilder - .getCommandBuilder('invokeFoo', 'invokes foo command') - .addPayloadSchema(theServiceV1InvokeFooInputPayloadSchema) - .addParameterSchema(theServiceV1InvokeFooInputParameterSchema) - .addOutputSchema(theServiceV1InvokeFooOutputPayloadSchema) - .canInvoke( - 'TheService', - '1', - 'foo', - z.object({ - payload: z.any(), - parameter: z.any(), - }), - ) - .setCommandFunction(async function ({ service }, payload, parameter) { - return service.TheService['1'].foo(payload, parameter) - }) + .getCommandBuilder('invokeFoo', 'invokes foo command') + .addPayloadSchema(theServiceV1InvokeFooInputPayloadSchema) + .addParameterSchema(theServiceV1InvokeFooInputParameterSchema) + .addOutputSchema(theServiceV1InvokeFooOutputPayloadSchema) + .canInvoke( + 'TheService', + '1', + 'foo', + z.object({ + payload: z.any(), + parameter: z.any(), + }), + ) + .setCommandFunction(async function ({ service }, payload, parameter) { + return service.TheService['1'].foo(payload, parameter) + }) diff --git a/test/service/theService/v1/command/invokeFoo/schema.ts b/test/service/theService/v1/command/invokeFoo/schema.ts index d833a6037..3155de86a 100644 --- a/test/service/theService/v1/command/invokeFoo/schema.ts +++ b/test/service/theService/v1/command/invokeFoo/schema.ts @@ -3,7 +3,7 @@ import { z } from 'zod' // define the input parameters export const theServiceV1InvokeFooInputParameterSchema = extendApi(z.any(), { - title: 'ping input parameter schema', + title: 'ping input parameter schema', }) // define the input payload @@ -11,11 +11,11 @@ export const theServiceV1InvokeFooInputPayloadSchema = extendApi(z.any(), { titl // define the output payload export const theServiceV1InvokeFooOutputPayloadSchema = extendApi( - z.object({ - payload: z.any(), - parameter: z.any(), - }), - { - title: 'ping output payload schema', - }, + z.object({ + payload: z.any(), + parameter: z.any(), + }), + { + title: 'ping output payload schema', + }, ) diff --git a/test/service/theService/v1/command/invokeFoo/types.ts b/test/service/theService/v1/command/invokeFoo/types.ts index d050fb7c5..f9ec6db0e 100644 --- a/test/service/theService/v1/command/invokeFoo/types.ts +++ b/test/service/theService/v1/command/invokeFoo/types.ts @@ -1,9 +1,9 @@ import type { z } from 'zod' import type { - theServiceV1InvokeFooInputParameterSchema, - theServiceV1InvokeFooInputPayloadSchema, - theServiceV1InvokeFooOutputPayloadSchema, + theServiceV1InvokeFooInputParameterSchema, + theServiceV1InvokeFooInputPayloadSchema, + theServiceV1InvokeFooOutputPayloadSchema, } from './schema.js' export type TheServiceV1InvokeFooInputParameter = z.input diff --git a/test/service/theService/v1/command/invokeFooFailed/invokeFooFailedCommandBuilder.ts b/test/service/theService/v1/command/invokeFooFailed/invokeFooFailedCommandBuilder.ts index 0d6d9e7b8..9c920d233 100644 --- a/test/service/theService/v1/command/invokeFooFailed/invokeFooFailedCommandBuilder.ts +++ b/test/service/theService/v1/command/invokeFooFailed/invokeFooFailedCommandBuilder.ts @@ -2,25 +2,25 @@ import { z } from 'zod' import { theServiceServiceBuilder } from '../../theServiceServiceBuilder.js' import { - theServiceV1InvokeFooInputParameterSchema, - theServiceV1InvokeFooInputPayloadSchema, - theServiceV1InvokeFooOutputPayloadSchema, + theServiceV1InvokeFooInputParameterSchema, + theServiceV1InvokeFooInputPayloadSchema, + theServiceV1InvokeFooOutputPayloadSchema, } from './schema.js' export const invokeFooFailedCommandBuilder = theServiceServiceBuilder - .getCommandBuilder('invokeFooFailed', 'invokes foo command with wrong invoke schema') - .addPayloadSchema(theServiceV1InvokeFooInputPayloadSchema) - .addParameterSchema(theServiceV1InvokeFooInputParameterSchema) - .addOutputSchema(theServiceV1InvokeFooOutputPayloadSchema) - .canInvoke( - 'TheService', - '1', - 'foo', - z.object({ - payload: z.number(), - parameter: z.number(), - }), - ) - .setCommandFunction(async function ({ service }, payload, parameter) { - return service.TheService['1'].foo(payload, parameter) - }) + .getCommandBuilder('invokeFooFailed', 'invokes foo command with wrong invoke schema') + .addPayloadSchema(theServiceV1InvokeFooInputPayloadSchema) + .addParameterSchema(theServiceV1InvokeFooInputParameterSchema) + .addOutputSchema(theServiceV1InvokeFooOutputPayloadSchema) + .canInvoke( + 'TheService', + '1', + 'foo', + z.object({ + payload: z.number(), + parameter: z.number(), + }), + ) + .setCommandFunction(async function ({ service }, payload, parameter) { + return service.TheService['1'].foo(payload, parameter) + }) diff --git a/test/service/theService/v1/command/invokeFooFailed/schema.ts b/test/service/theService/v1/command/invokeFooFailed/schema.ts index d833a6037..3155de86a 100644 --- a/test/service/theService/v1/command/invokeFooFailed/schema.ts +++ b/test/service/theService/v1/command/invokeFooFailed/schema.ts @@ -3,7 +3,7 @@ import { z } from 'zod' // define the input parameters export const theServiceV1InvokeFooInputParameterSchema = extendApi(z.any(), { - title: 'ping input parameter schema', + title: 'ping input parameter schema', }) // define the input payload @@ -11,11 +11,11 @@ export const theServiceV1InvokeFooInputPayloadSchema = extendApi(z.any(), { titl // define the output payload export const theServiceV1InvokeFooOutputPayloadSchema = extendApi( - z.object({ - payload: z.any(), - parameter: z.any(), - }), - { - title: 'ping output payload schema', - }, + z.object({ + payload: z.any(), + parameter: z.any(), + }), + { + title: 'ping output payload schema', + }, ) diff --git a/test/service/theService/v1/command/invokeFooFailed/types.ts b/test/service/theService/v1/command/invokeFooFailed/types.ts index d050fb7c5..f9ec6db0e 100644 --- a/test/service/theService/v1/command/invokeFooFailed/types.ts +++ b/test/service/theService/v1/command/invokeFooFailed/types.ts @@ -1,9 +1,9 @@ import type { z } from 'zod' import type { - theServiceV1InvokeFooInputParameterSchema, - theServiceV1InvokeFooInputPayloadSchema, - theServiceV1InvokeFooOutputPayloadSchema, + theServiceV1InvokeFooInputParameterSchema, + theServiceV1InvokeFooInputPayloadSchema, + theServiceV1InvokeFooOutputPayloadSchema, } from './schema.js' export type TheServiceV1InvokeFooInputParameter = z.input diff --git a/test/service/theService/v1/command/ping/pingCommandBuilder.ts b/test/service/theService/v1/command/ping/pingCommandBuilder.ts index d40d31eb9..f16f2c7bc 100644 --- a/test/service/theService/v1/command/ping/pingCommandBuilder.ts +++ b/test/service/theService/v1/command/ping/pingCommandBuilder.ts @@ -1,19 +1,19 @@ import { theServiceServiceBuilder } from '../../theServiceServiceBuilder.js' import { - theServiceV1PingInputParameterSchema, - theServiceV1PingInputPayloadSchema, - theServiceV1PingOutputPayloadSchema, + theServiceV1PingInputParameterSchema, + theServiceV1PingInputPayloadSchema, + theServiceV1PingOutputPayloadSchema, } from './schema.js' export const pingCommandBuilder = theServiceServiceBuilder - .getCommandBuilder('ping', 'provide a dummy command') - .addPayloadSchema(theServiceV1PingInputPayloadSchema) - .addParameterSchema(theServiceV1PingInputParameterSchema) - .addOutputSchema(theServiceV1PingOutputPayloadSchema) - .exposeAsHttpEndpoint('GET', 'ping') - .addQueryParameters({ name: 'param', required: false }, { required: true, name: 'required' }) - .setCommandFunction(async function (_context, _payload, _parameter) { - return { - ping: true, - } - }) + .getCommandBuilder('ping', 'provide a dummy command') + .addPayloadSchema(theServiceV1PingInputPayloadSchema) + .addParameterSchema(theServiceV1PingInputParameterSchema) + .addOutputSchema(theServiceV1PingOutputPayloadSchema) + .exposeAsHttpEndpoint('GET', 'ping') + .addQueryParameters({ name: 'param', required: false }, { required: true, name: 'required' }) + .setCommandFunction(async function (_context, _payload, _parameter) { + return { + ping: true, + } + }) diff --git a/test/service/theService/v1/command/ping/schema.ts b/test/service/theService/v1/command/ping/schema.ts index b20b855b7..a49cf1bdd 100644 --- a/test/service/theService/v1/command/ping/schema.ts +++ b/test/service/theService/v1/command/ping/schema.ts @@ -3,11 +3,11 @@ import { z } from 'zod' // define the input parameters export const theServiceV1PingInputParameterSchema = extendApi( - z.object({ - param: z.string().optional(), - required: z.string(), - }), - { title: 'ping input parameter schema' }, + z.object({ + param: z.string().optional(), + required: z.string(), + }), + { title: 'ping input parameter schema' }, ) // define the input payload @@ -15,5 +15,5 @@ export const theServiceV1PingInputPayloadSchema = extendApi(z.undefined(), { tit // define the output payload export const theServiceV1PingOutputPayloadSchema = extendApi(z.object({ ping: z.boolean() }), { - title: 'ping output payload schema', + title: 'ping output payload schema', }) diff --git a/test/service/theService/v1/command/ping/types.ts b/test/service/theService/v1/command/ping/types.ts index babb58f4b..1aacbf07a 100644 --- a/test/service/theService/v1/command/ping/types.ts +++ b/test/service/theService/v1/command/ping/types.ts @@ -1,9 +1,9 @@ import type { z } from 'zod' import type { - theServiceV1PingInputParameterSchema, - theServiceV1PingInputPayloadSchema, - theServiceV1PingOutputPayloadSchema, + theServiceV1PingInputParameterSchema, + theServiceV1PingInputPayloadSchema, + theServiceV1PingOutputPayloadSchema, } from './schema.js' export type TheServiceV1PingInputParameter = z.input diff --git a/test/service/theService/v1/theServiceServiceBuilder.ts b/test/service/theService/v1/theServiceServiceBuilder.ts index 3a0b86235..ea7eb8dee 100644 --- a/test/service/theService/v1/theServiceServiceBuilder.ts +++ b/test/service/theService/v1/theServiceServiceBuilder.ts @@ -5,12 +5,12 @@ import { generalTheServiceServiceInfo } from '../generalTheServiceServiceInfo.js import { theServiceServiceV1ConfigSchema } from './theServiceServiceConfig.js' export const theServiceServiceInfo = { - serviceVersion: '1', - ...generalTheServiceServiceInfo, + serviceVersion: '1', + ...generalTheServiceServiceInfo, } as const satisfies ServiceInfoType // create a service builder instance and assign service config schema and default config. -export const theServiceServiceBuilder = new ServiceBuilder(theServiceServiceInfo) - .setConfigSchema(theServiceServiceV1ConfigSchema) - .setDefaultConfig({}) +export const theServiceServiceBuilder = new ServiceBuilder(theServiceServiceInfo).setConfigSchema( + theServiceServiceV1ConfigSchema, +) diff --git a/test/service/theService/v1/theServiceV1Service.ts b/test/service/theService/v1/theServiceV1Service.ts index e52f59ab6..4177a8972 100644 --- a/test/service/theService/v1/theServiceV1Service.ts +++ b/test/service/theService/v1/theServiceV1Service.ts @@ -11,14 +11,14 @@ import { theServiceServiceBuilder } from './theServiceServiceBuilder.js' // other service config should be done in ./theServiceServiceBuilder.ts file const commandDefinitions: CommandDefinitionList = [ - pingCommandBuilder.getDefinition(), - fooCommandBuilder.getDefinition(), - invokeFooCommandBuilder.getDefinition(), - invokeFooFailedCommandBuilder.getDefinition(), + pingCommandBuilder.getDefinition(), + fooCommandBuilder.getDefinition(), + invokeFooCommandBuilder.getDefinition(), + invokeFooFailedCommandBuilder.getDefinition(), ] const subscriptionDefinitions: SubscriptionDefinitionList = [] export const theServiceV1Service = theServiceServiceBuilder - .addCommandDefinition(...commandDefinitions) - .addSubscriptionDefinition(...subscriptionDefinitions) + .addCommandDefinition(...commandDefinitions) + .addSubscriptionDefinition(...subscriptionDefinitions) diff --git a/tsconfig.json b/tsconfig.json index 8645e0fd3..ae021a4ad 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,35 +1,29 @@ { - "$schema": "https://json.schemastore.org/tsconfig", - "display": "Node 18", - "compilerOptions": { - "strictNullChecks": true, - "outDir": "dist", - "strict": true, - "module": "es2022", - "declaration": true, - "removeComments": false, - "emitDecoratorMetadata": true, - "experimentalDecorators": true, - "moduleResolution": "Node", - "allowSyntheticDefaultImports": true, - "target": "es2022", - "sourceMap": true, - "incremental": true, - "noImplicitAny": true, - "esModuleInterop": true, - "resolveJsonModule": true, - "skipLibCheck": true, - "forceConsistentCasingInFileNames": true, - "declarationMap": true, - "types": [ - "vitest/globals", - "node" - ], - "noFallthroughCasesInSwitch": true, - "useUnknownInCatchVariables": true - }, - "exclude": [ - "node_modules", - "dist" - ] + "$schema": "https://json.schemastore.org/tsconfig", + "display": "Node 18", + "compilerOptions": { + "strictNullChecks": true, + "outDir": "dist", + "strict": true, + "module": "es2022", + "declaration": true, + "removeComments": false, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "moduleResolution": "Node", + "allowSyntheticDefaultImports": true, + "target": "es2022", + "sourceMap": true, + "incremental": true, + "noImplicitAny": true, + "esModuleInterop": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "declarationMap": true, + "types": ["vitest/globals", "node"], + "noFallthroughCasesInSwitch": true, + "useUnknownInCatchVariables": true + }, + "exclude": ["node_modules", "dist"] } diff --git a/typedoc.base.json b/typedoc.base.json index c43f97308..df4e9dc8d 100644 --- a/typedoc.base.json +++ b/typedoc.base.json @@ -1,8 +1,15 @@ { - "hideGenerator": true, - "includeVersion":true, - "excludeInternal": true, - "excludeExternals": true, - "exclude": ["**/**/*.(test).ts","**/*+(index|.spec|.e2e|.test).ts","lib/**","/examples/**/*","node_modules","**/node_modules"], - "gitRevision": "master" -} \ No newline at end of file + "hideGenerator": true, + "includeVersion": true, + "excludeInternal": true, + "excludeExternals": true, + "exclude": [ + "**/**/*.(test).ts", + "**/*+(index|.spec|.e2e|.test).ts", + "lib/**", + "/examples/**/*", + "node_modules", + "**/node_modules" + ], + "gitRevision": "master" +} diff --git a/typedoc.json b/typedoc.json index 1382edfdb..d439dbc5e 100644 --- a/typedoc.json +++ b/typedoc.json @@ -1,33 +1,41 @@ { - "name":"PURISTA API", - "entryPointStrategy":"packages", - "entryPoints": [ - "./packages/amqpbridge", - "./packages/aws-config-store", - "./packages/aws-secret-store", - "./packages/azure-secret-store", - "./packages/base-http-bridge", - "./packages/cli", - "./packages/core", - "./packages/dapr-sdk", - "./packages/gcloud-secret-store", - "./packages/hono-http-server", - "./packages/httpserver", - "./packages/infisical-secret-store", - "./packages/k8s-sdk", - "./packages/mqttbridge", - "./packages/nats-config-store", - "./packages/nats-state-store", - "./packages/natsbridge", - "./packages/redis-config-store", - "./packages/redis-state-store" - ], - "plugin": ["typedoc-plugin-markdown"], - "out": "website/doc/api", - "readme":"CHANGELOG.md", - "hideGenerator": true, - "includeVersion":true, - "excludeInternal": true, - "excludeExternals": true, - "exclude": ["**/**/*.(test).ts","**/*+(index|.spec|.e2e|.test).ts","dist/**","lib/**","/examples/**/*","node_modules","**/node_modules"] -} \ No newline at end of file + "name": "PURISTA API", + "entryPointStrategy": "packages", + "entryPoints": [ + "./packages/amqpbridge", + "./packages/aws-config-store", + "./packages/aws-secret-store", + "./packages/azure-secret-store", + "./packages/base-http-bridge", + "./packages/cli", + "./packages/core", + "./packages/dapr-sdk", + "./packages/gcloud-secret-store", + "./packages/hono-http-server", + "./packages/httpserver", + "./packages/infisical-secret-store", + "./packages/k8s-sdk", + "./packages/mqttbridge", + "./packages/nats-config-store", + "./packages/nats-state-store", + "./packages/natsbridge", + "./packages/redis-config-store", + "./packages/redis-state-store" + ], + "plugin": ["typedoc-plugin-markdown"], + "out": "website/doc/api", + "readme": "CHANGELOG.md", + "hideGenerator": true, + "includeVersion": true, + "excludeInternal": true, + "excludeExternals": true, + "exclude": [ + "**/**/*.(test).ts", + "**/*+(index|.spec|.e2e|.test).ts", + "dist/**", + "lib/**", + "/examples/**/*", + "node_modules", + "**/node_modules" + ] +} diff --git a/vite.config.all.ts b/vite.config.all.ts deleted file mode 100644 index 5d51f7538..000000000 --- a/vite.config.all.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { defineConfig } from 'vitest/config' - -export default defineConfig({ - test: { - isolate: true, - globals: true, - watch: false, - environment: 'node', - hookTimeout: 30000, - coverage: { - enabled: true, - include: ['**/src/**/*.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], - exclude: ['examples/**', 'packages/cli/**', 'website:/**'], - thresholds: { - lines: 80, - functions: 80, - branches: 70, - statements: 80, - }, - }, - include: [ - '**/test/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}', - '**/src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}', - ], - exclude: ['**/node_modules/**', '**/dist/**', '**/.tshy-build/**'], - }, -}) diff --git a/vite.config.integration.ts b/vite.config.integration.ts deleted file mode 100644 index 2ba5e7499..000000000 --- a/vite.config.integration.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { defineConfig } from 'vitest/config' - -export default defineConfig({ - test: { - isolate: true, - globals: true, - watch: false, - environment: 'node', - hookTimeout: 30000, - coverage: { - enabled: false, - include: ['**/src/**/*.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], - }, - include: ['**/test/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], - exclude: ['**/node_modules/**', '**/dist/**', '**/.tshy-build/**', '**/src/**'], - }, -}) diff --git a/vite.config.ts b/vite.config.ts deleted file mode 100644 index c731ed781..000000000 --- a/vite.config.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { defineConfig } from 'vitest/config' - -export default defineConfig({ - test: { - isolate: true, - globals: true, - watch: false, - environment: 'node', - coverage: { - enabled: true, - include: ['**/src/**/*.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], - exclude: ['examples/**', 'packages/cli/**', 'website:/**'], - thresholds: { - lines: 63, - functions: 63, - branches: 74, - statements: 63, - }, - }, - include: [ - '**/src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}', - // '**/test/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}', - ], - exclude: ['**/node_modules/**', '**/dist/**', '**/.tshy-build/**', '**/test/**', 'website/**'], - }, -}) diff --git a/vitest.config.ts b/vitest.config.ts new file mode 100644 index 000000000..cf95f603f --- /dev/null +++ b/vitest.config.ts @@ -0,0 +1,28 @@ +import { configDefaults, defineConfig } from 'vitest/config' + +export default defineConfig({ + test: { + isolate: false, + globals: true, + watch: false, + environment: 'node', + testTimeout: 30_000, + hookTimeout: 30_000, + coverage: { + enabled: false, + include: ['**/src/**/*.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], + exclude: ['examples/**', 'packages/cli/**', 'website:/**'], + thresholds: { + lines: 63, + functions: 63, + branches: 74, + statements: 63, + }, + }, + include: [ + '**/test/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}', + '**/src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}', + ], + exclude: [...configDefaults.exclude, '**/node_modules/**', '**/dist/**', '**/.tshy-build/**', 'website/**'], + }, +}) diff --git a/vitest.config.unit.ts b/vitest.config.unit.ts new file mode 100644 index 000000000..e6d790648 --- /dev/null +++ b/vitest.config.unit.ts @@ -0,0 +1,32 @@ +import { configDefaults, defineConfig } from 'vitest/config' + +export default defineConfig({ + test: { + isolate: false, + globals: true, + watch: false, + environment: 'node', + testTimeout: 30_000, + hookTimeout: 30_000, + coverage: { + enabled: false, + include: ['**/src/**/*.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], + exclude: ['examples/**', 'packages/cli/**', 'website:/**'], + thresholds: { + lines: 63, + functions: 63, + branches: 74, + statements: 63, + }, + }, + include: ['**/src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], + exclude: [ + ...configDefaults.exclude, + '**/test/**', + '**/node_modules/**', + '**/dist/**', + '**/.tshy-build/**', + 'website/**', + ], + }, +}) diff --git a/website/doc/.vitepress/config.mts b/website/doc/.vitepress/config.mts index 0b402ba62..605e45ae4 100644 --- a/website/doc/.vitepress/config.mts +++ b/website/doc/.vitepress/config.mts @@ -1,305 +1,305 @@ -import { HeadConfig, SiteConfig, createContentLoader, defineConfig } from 'vitepress' -import { generateSidebar } from 'vitepress-sidebar'; -import path from 'path' -import { writeFileSync } from 'fs' +import { writeFileSync } from 'node:fs' +import path from 'node:path' import { Feed } from 'feed' +import { type HeadConfig, type SiteConfig, createContentLoader, defineConfig } from 'vitepress' +import { generateSidebar } from 'vitepress-sidebar' const hostname: string = 'https://purista.dev' // https://vitepress.dev/reference/site-config export default defineConfig({ - outDir: '../../docs', - lang: 'en-US', - head: [ - ['link', { rel: 'stylesheet', type: 'text/css', media: 'all', href: '/cookieconsent.css' }], - ['script', { src: '/cookieconsent2.js' }], - ['script', { src: '/cookieconsent-init2.js'}], - ], - title: "PURISTA", - //appearance: false, - description: "The typescript/javascript framework for building nodejs backend services and api in modern, modular and scalable way", - sitemap: { - hostname: 'https://purista.dev' - }, - transformHead: ({ pageData,page }) => { - const head: HeadConfig[] = [] + outDir: '../../docs', + lang: 'en-US', + head: [ + ['link', { rel: 'stylesheet', type: 'text/css', media: 'all', href: '/cookieconsent.css' }], + ['script', { src: '/cookieconsent2.js' }], + ['script', { src: '/cookieconsent-init2.js' }], + ], + title: 'PURISTA', + //appearance: false, + description: + 'The typescript/javascript framework for building nodejs backend services and api in modern, modular and scalable way', + sitemap: { + hostname: 'https://purista.dev', + }, + transformHead: ({ pageData, page }) => { + const head: HeadConfig[] = [] - head.push(['meta', { property: 'og:title', content: pageData.frontmatter.title }]) - head.push(['meta', { property: 'og:url', content: new URL(page.replace('.md','.html'),hostname).toString() }]) - head.push(['meta', { property: 'og:description', content: pageData.frontmatter.description }]) - head.push(['meta', { property: 'og:type', content: 'website' }]) - head.push(['meta', { property: 'twitter:card', content: 'summary_large_image' }]) - head.push(['meta', { property: 'twitter:site', content: '@purista_js' }]) - head.push(['meta', { property: 'twitter:creator', content: '@purista_js' }]) + head.push(['meta', { property: 'og:title', content: pageData.frontmatter.title }]) + head.push(['meta', { property: 'og:url', content: new URL(page.replace('.md', '.html'), hostname).toString() }]) + head.push(['meta', { property: 'og:description', content: pageData.frontmatter.description }]) + head.push(['meta', { property: 'og:type', content: 'website' }]) + head.push(['meta', { property: 'twitter:card', content: 'summary_large_image' }]) + head.push(['meta', { property: 'twitter:site', content: '@purista_js' }]) + head.push(['meta', { property: 'twitter:creator', content: '@purista_js' }]) - if(pageData.frontmatter.image) { - head.push(['meta', { property: 'og:image', content: new URL(pageData.frontmatter.image,hostname).toString() }]) - }else { - head.push(['meta', { property: 'og:image', content: `https://ogpreview-ten.vercel.app/api/og?title=${encodeURIComponent(pageData.frontmatter.title)}&description=${encodeURIComponent(pageData.frontmatter.description)}` }]) - } + if (pageData.frontmatter.image) { + head.push(['meta', { property: 'og:image', content: new URL(pageData.frontmatter.image, hostname).toString() }]) + } else { + head.push([ + 'meta', + { + property: 'og:image', + content: `https://ogpreview-ten.vercel.app/api/og?title=${encodeURIComponent(pageData.frontmatter.title)}&description=${encodeURIComponent(pageData.frontmatter.description)}`, + }, + ]) + } - return head - }, + return head + }, - ignoreDeadLinks: [ - /^https?:\/\/localhost/, - /^https?:\/\/127.0.0.1/, - ], + ignoreDeadLinks: [/^https?:\/\/localhost/, /^https?:\/\/127.0.0.1/], - themeConfig: { - // https://vitepress.dev/reference/default-theme-config - search: { - provider: 'local' - }, - logo: '/purista_logo.png', - editLink: { - pattern: 'https://github.com/sebastianwessel/purista/tree/master/website/doc/:path', - text: 'Edit this page on GitHub' - }, - outline: 'deep', - lastUpdated: { - text: 'Updated at', - formatOptions: { - dateStyle: 'full', - timeStyle: 'medium' - } - }, - nav: [ - { text: 'Home', link: '/' }, - { text: 'Handbook', link: '/handbook/', activeMatch: '/handbook/'}, - { text: 'Blog', link: '/article/' , activeMatch: '/article/'}, - { text: 'API', link: '/api/README', activeMatch: '/api/' }, - { text: 'Resources', link: '/resources/', activeMatch: '/resources/' }, - ], + themeConfig: { + // https://vitepress.dev/reference/default-theme-config + search: { + provider: 'local', + }, + logo: '/purista_logo.png', + editLink: { + pattern: 'https://github.com/puristajs/purista/tree/master/website/doc/:path', + text: 'Edit this page on GitHub', + }, + outline: 'deep', + lastUpdated: { + text: 'Updated at', + formatOptions: { + dateStyle: 'full', + timeStyle: 'medium', + }, + }, + nav: [ + { text: 'Home', link: '/' }, + { text: 'Handbook', link: '/handbook/', activeMatch: '/handbook/' }, + { text: 'Blog', link: '/article/', activeMatch: '/article/' }, + { text: 'API', link: '/api/README', activeMatch: '/api/' }, + { text: 'Resources', link: '/resources/', activeMatch: '/resources/' }, + ], - sidebar: { - ...generateSidebar([ - { - documentRootPath: 'doc', - scanStartPath: 'handbook', - resolvePath: '/handbook/', - useTitleFromFileHeading: true, - hyphenToSpace: true, - capitalizeFirst: true, - underscoreToSpace: true, - useTitleFromFrontmatter: true, - useFolderTitleFromIndexFile: true, - useFolderLinkFromIndexFile: true, - sortMenusByFrontmatterOrder: true, - includeRootIndexFile: true, - includeEmptyFolder: true, - capitalizeEachWords: true, - collapseDepth: 1, - }, - { - documentRootPath: 'doc', - scanStartPath: 'article', - resolvePath: '/article/', - useTitleFromFileHeading: true, - hyphenToSpace: true, - capitalizeFirst: true, - underscoreToSpace: true, - useTitleFromFrontmatter: true, - useFolderTitleFromIndexFile: true, - useFolderLinkFromIndexFile: true, - sortMenusByFrontmatterOrder: true, - includeRootIndexFile: true, - includeEmptyFolder: true, - capitalizeEachWords: true, - collapseDepth: 2, - sortMenusOrderByDescending: true - }, - { - documentRootPath: 'doc', - scanStartPath: 'resources', - resolvePath: '/resources/', - useTitleFromFileHeading: true, - hyphenToSpace: true, - capitalizeFirst: true, - underscoreToSpace: true, - useTitleFromFrontmatter: true, - useFolderTitleFromIndexFile: true, - useFolderLinkFromIndexFile: true, - sortMenusByFrontmatterOrder: true, - includeRootIndexFile: true, - includeEmptyFolder: true, - capitalizeEachWords: true, - collapseDepth: 2, - } - ]), - 'api': [ - { - text: 'Core', - items: [ - { - text: '@purista/core', - link: '/api/modules/purista_core.md', - }, - { - text: '@purista/hono-http-server', - link: '/api/modules/purista_hono_http_server.md', - }, - { - text: '@purista/httpserver', - link: '/api/modules/purista_httpserver.md', - }, - ], - }, - { - text: 'Event bridges', - items: [ - { - text: '@purista/amqpbridge', - link: '/api/modules/purista_amqpbridge.md', - }, - { - text: '@purista/dapr-sdk', - link: '/api/modules/purista_dapr_sdk.md', - }, - { - text: '@purista/mqttbridge', - link: '/api/modules/purista_mqttbridge.md', - }, - { - text: '@purista/natsbridge', - link: '/api/modules/purista_natsbridge.md', - }, - ], - }, - { - text: 'Config stores', - items: [ - { - text: '@purista/aws-config-store', - link: '/api/modules/purista_aws_config_store.md', - }, - { - text: '@purista/dapr-sdk', - link: '/api/modules/purista_dapr_sdk.md', - }, - { - text: '@purista/nats-config-store', - link: '/api/modules/purista_nats_config_store.md', - }, - { - text: '@purista/redis-config-store', - link: '/api/modules/purista_redis_config_store.md', - }, - ], - }, - { - text: 'Secret stores', - items: [ - { - text: '@purista/aws-secret-store', - link: '/api/modules/purista_aws_secret_store.md', - }, - { - text: '@purista/azure-secret-store', - link: '/api/modules/purista_azure_secret_store.md', - }, - { - text: '@purista/dapr-sdk', - link: '/api/modules/purista_dapr_sdk.md', - }, - { - text: '@purista/gcloud-secret-store', - link: '/api/modules/purista_gcloud_secret_store.md', - }, - { - text: '@purista/infisical-secret-store', - link: '/api/modules/purista_infisical_secret_store.md', - }, - ], - }, - { - text: 'State stores', - items: [ - { - text: '@purista/dapr-sdk', - link: '/api/modules/purista_dapr_sdk.md', - }, - { - text: '@purista/nats-state-store', - link: '/api/modules/purista_nats_state_store.md', - }, - { - text: '@purista/redis-state-store', - link: '/api/modules/purista_redis_state_store.md', - }, - ], - }, - { - text: 'Deployment SDK', - items: [ - { - text: '@purista/dapr-sdk', - link: '/api/modules/purista_dapr_sdk.md', - }, - { - text: '@purista/k8s-sdk', - link: '/api/modules/purista_k8s_sdk.md', - }, - ], - }, - ] - }, + sidebar: { + ...generateSidebar([ + { + documentRootPath: 'doc', + scanStartPath: 'handbook', + resolvePath: '/handbook/', + useTitleFromFileHeading: true, + hyphenToSpace: true, + capitalizeFirst: true, + underscoreToSpace: true, + useTitleFromFrontmatter: true, + useFolderTitleFromIndexFile: true, + useFolderLinkFromIndexFile: true, + sortMenusByFrontmatterOrder: true, + includeRootIndexFile: true, + includeEmptyFolder: true, + capitalizeEachWords: true, + collapseDepth: 1, + }, + { + documentRootPath: 'doc', + scanStartPath: 'article', + resolvePath: '/article/', + useTitleFromFileHeading: true, + hyphenToSpace: true, + capitalizeFirst: true, + underscoreToSpace: true, + useTitleFromFrontmatter: true, + useFolderTitleFromIndexFile: true, + useFolderLinkFromIndexFile: true, + sortMenusByFrontmatterOrder: true, + includeRootIndexFile: true, + includeEmptyFolder: true, + capitalizeEachWords: true, + collapseDepth: 2, + sortMenusOrderByDescending: true, + }, + { + documentRootPath: 'doc', + scanStartPath: 'resources', + resolvePath: '/resources/', + useTitleFromFileHeading: true, + hyphenToSpace: true, + capitalizeFirst: true, + underscoreToSpace: true, + useTitleFromFrontmatter: true, + useFolderTitleFromIndexFile: true, + useFolderLinkFromIndexFile: true, + sortMenusByFrontmatterOrder: true, + includeRootIndexFile: true, + includeEmptyFolder: true, + capitalizeEachWords: true, + collapseDepth: 2, + }, + ]), + api: [ + { + text: 'Core', + items: [ + { + text: '@purista/core', + link: '/api/modules/purista_core.md', + }, + { + text: '@purista/hono-http-server', + link: '/api/modules/purista_hono_http_server.md', + }, + { + text: '@purista/httpserver', + link: '/api/modules/purista_httpserver.md', + }, + ], + }, + { + text: 'Event bridges', + items: [ + { + text: '@purista/amqpbridge', + link: '/api/modules/purista_amqpbridge.md', + }, + { + text: '@purista/dapr-sdk', + link: '/api/modules/purista_dapr_sdk.md', + }, + { + text: '@purista/mqttbridge', + link: '/api/modules/purista_mqttbridge.md', + }, + { + text: '@purista/natsbridge', + link: '/api/modules/purista_natsbridge.md', + }, + ], + }, + { + text: 'Config stores', + items: [ + { + text: '@purista/aws-config-store', + link: '/api/modules/purista_aws_config_store.md', + }, + { + text: '@purista/dapr-sdk', + link: '/api/modules/purista_dapr_sdk.md', + }, + { + text: '@purista/nats-config-store', + link: '/api/modules/purista_nats_config_store.md', + }, + { + text: '@purista/redis-config-store', + link: '/api/modules/purista_redis_config_store.md', + }, + ], + }, + { + text: 'Secret stores', + items: [ + { + text: '@purista/aws-secret-store', + link: '/api/modules/purista_aws_secret_store.md', + }, + { + text: '@purista/azure-secret-store', + link: '/api/modules/purista_azure_secret_store.md', + }, + { + text: '@purista/dapr-sdk', + link: '/api/modules/purista_dapr_sdk.md', + }, + { + text: '@purista/gcloud-secret-store', + link: '/api/modules/purista_gcloud_secret_store.md', + }, + { + text: '@purista/infisical-secret-store', + link: '/api/modules/purista_infisical_secret_store.md', + }, + ], + }, + { + text: 'State stores', + items: [ + { + text: '@purista/dapr-sdk', + link: '/api/modules/purista_dapr_sdk.md', + }, + { + text: '@purista/nats-state-store', + link: '/api/modules/purista_nats_state_store.md', + }, + { + text: '@purista/redis-state-store', + link: '/api/modules/purista_redis_state_store.md', + }, + ], + }, + { + text: 'Deployment SDK', + items: [ + { + text: '@purista/dapr-sdk', + link: '/api/modules/purista_dapr_sdk.md', + }, + { + text: '@purista/k8s-sdk', + link: '/api/modules/purista_k8s_sdk.md', + }, + ], + }, + ], + }, - socialLinks: [ - { icon: 'github', link: 'https://github.com/sebastianwessel/purista' }, - { icon: 'twitter', link: 'https://twitter.com/purista_js' }, - { icon: 'discord', link: 'https://discord.gg/9feaUm3H2v' } - ], - footer: { - message: 'Made from developers for developers with ❤️', - copyright: 'Privacy | Cookie preferences | Imprint' - } - }, + socialLinks: [ + { icon: 'github', link: 'https://github.com/puristajs/purista' }, + { icon: 'twitter', link: 'https://twitter.com/purista_js' }, + { icon: 'discord', link: 'https://discord.gg/9feaUm3H2v' }, + ], + footer: { + message: 'Made from developers for developers with ❤️', + copyright: + 'Privacy | Cookie preferences | Imprint', + }, + }, - buildEnd: async (config: SiteConfig) => { - const feed = new Feed({ - title: 'PURISTA Blog', - description: 'Offical news, updates and announcements for PURISTA typescript backend framework', - id: hostname, - link: hostname, - language: 'en', - image: 'https://purista.dev', - favicon: `${hostname}/favicon.ico`, - copyright: - 'Copyright (c) 2023-present, Sebastian Wessel' - }) + buildEnd: async (config: SiteConfig) => { + const feed = new Feed({ + title: 'PURISTA Blog', + description: 'Official news, updates and announcements for PURISTA typescript backend framework', + id: hostname, + link: hostname, + language: 'en', + image: 'https://purista.dev', + favicon: `${hostname}/favicon.ico`, + copyright: 'Copyright (c) 2023-present, Sebastian Wessel', + }) - // You might need to adjust this if your Markdown files - // are located in a subfolder - const posts = await createContentLoader('*.md', { - excerpt: true, - render: true - }).load() + // You might need to adjust this if your Markdown files + // are located in a subfolder + const posts = await createContentLoader('*.md', { + excerpt: true, + render: true, + }).load() - posts.sort( - (a, b) => - +new Date(b.frontmatter.date as string) - - +new Date(a.frontmatter.date as string) - ) + posts.sort((a, b) => +new Date(b.frontmatter.date as string) - +new Date(a.frontmatter.date as string)) - for (const { url, excerpt, frontmatter, html } of posts) { - feed.addItem({ - title: frontmatter.title, - id: `${hostname}${url}`, - link: `${hostname}${url}`, - description: excerpt, - content: html, - author: [ - { - name: 'Sebastian Wessel', - email: 'info@purista.dev', - link: 'https://sebastianwessel.de' - } - ], - date: frontmatter.date - }) - } + for (const { url, excerpt, frontmatter, html } of posts) { + feed.addItem({ + title: frontmatter.title, + id: `${hostname}${url}`, + link: `${hostname}${url}`, + description: excerpt, + content: html, + author: [ + { + name: 'Sebastian Wessel', + email: 'info@purista.dev', + link: 'https://sebastianwessel.de', + }, + ], + date: frontmatter.date, + }) + } - writeFileSync(path.join(config.outDir, 'feed.rss'), feed.rss2()) - } + writeFileSync(path.join(config.outDir, 'feed.rss'), feed.rss2()) + }, }) diff --git a/website/doc/.vitepress/theme/components/ExternalLink.ts b/website/doc/.vitepress/theme/components/ExternalLink.ts index a7580ef10..cab1d9b9c 100644 --- a/website/doc/.vitepress/theme/components/ExternalLink.ts +++ b/website/doc/.vitepress/theme/components/ExternalLink.ts @@ -2,9 +2,8 @@ import type { AnchorHTMLAttributes } from 'react' import type { FunctionalComponent } from 'vue' import { h } from 'vue' -export interface ExternalLinkProps - extends Omit, 'target'> { - href: string +export interface ExternalLinkProps extends Omit, 'target'> { + href: string } export const LOCAL_URL_REG = /^https?:\/\/localhost:/ @@ -12,21 +11,17 @@ export const LOCAL_URL_REG = /^https?:\/\/localhost:/ /** * @see https://github.com/vuejs/vitepress/issues/822 for details */ -export const ExternalLink: FunctionalComponent = ({ - href, - ...props -}) => { - if (process.env.NODE_ENV === 'development' && !LOCAL_URL_REG.test(href)) { - console.error('You should use markdown style link instead') - } - return h( - 'a', - { - href, - target: '_blank', - rel: 'noreferrer noopener', - ...props, - }, - href, - ) +export const ExternalLink: FunctionalComponent = ({ href, ...props }) => { + if (process.env.NODE_ENV === 'development' && !LOCAL_URL_REG.test(href)) { + } + return h( + 'a', + { + href, + target: '_blank', + rel: 'noreferrer noopener', + ...props, + }, + href, + ) } diff --git a/website/doc/.vitepress/theme/components/blog/Post.vue b/website/doc/.vitepress/theme/components/blog/Post.vue index afefe364c..74d9fb289 100644 --- a/website/doc/.vitepress/theme/components/blog/Post.vue +++ b/website/doc/.vitepress/theme/components/blog/Post.vue @@ -3,11 +3,11 @@ import { useData } from 'vitepress' import type { Post } from '../../composables/posts.data.js' const props = defineProps<{ - post: Post + post: Post }>() const { site } = useData() const getImgUrl = (title: string, description: string) => - `https://ogpreview-ten.vercel.app/api/og?title=${encodeURIComponent(title)}&description=${encodeURIComponent(description)}` + `https://ogpreview-ten.vercel.app/api/og?title=${encodeURIComponent(title)}&description=${encodeURIComponent(description)}`