diff --git a/.eslintrc.json b/.eslintrc.json index 81e3c213..a3d1cc56 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,6 +1,6 @@ { "root": true, - "ignorePatterns": ["**/dist/**/*", "**/node_modules/**/*","**/coverage/**/*", "**/*config*", "packages/create-jitar/templates/*"], + "ignorePatterns": ["**/dist/**/*", "**/node_modules/**/*","**/coverage/**/*", "packages/create-jitar/templates/*"], "parser": "@typescript-eslint/parser", "parserOptions": { "project": "./packages/**/tsconfig.json" diff --git a/examples/README.md b/examples/README.md index 203ea947..82baa8b6 100644 --- a/examples/README.md +++ b/examples/README.md @@ -31,15 +31,13 @@ The following examples focus on one particular concept at a time. The examples a 1. [Multi Version](concepts/multi-version/README.md) - Demonstrates how to create multiple versions of a procedure. 1. [Data Transportation](concepts/data-transportation/README.md) - Demonstrates how to transport data between segmented procedures. 1. [Error handling](concepts/error-handling/README.md) - Demonstrates how to the error handling works. -1. [Node client](concepts/node-client/README.md) - Demonstrates how to start a Jitar server and client. 1. [Health checks](concepts/health-checks/README.md) - Demonstrates how to use health checks. 1. [Middleware](concepts/middleware/README.md) - Demonstrates how to use middleware. 1. [Cors](concepts/cors/README.md) - Demonstrates how to enable cors. 1. [Construction](concepts/construction/README.md) - Demonstrates how to use a set up and tear down scripts. -1. [Overrides](concepts/overrides/README.md) - Demonstrates how to use import overrides. ## Application examples The following examples demonstrate how to build real-world applications using Jitar. -1. [Contact list](apps/contact-list/README.md) - Example of a full stack application build with React, MongoDB and Jitar. +1. [Full stack](apps/full-stack/README.md) - Example of a full stack application build with React, MongoDB and Jitar. diff --git a/examples/apps/full-stack/jitar.json b/examples/apps/full-stack/jitar.json new file mode 100644 index 00000000..b4c921fe --- /dev/null +++ b/examples/apps/full-stack/jitar.json @@ -0,0 +1,4 @@ +{ + "source": "./dist", + "target": "./dist" +} \ No newline at end of file diff --git a/examples/apps/full-stack/package.json b/examples/apps/full-stack/package.json index cf1069df..4a4ab46a 100644 --- a/examples/apps/full-stack/package.json +++ b/examples/apps/full-stack/package.json @@ -5,12 +5,14 @@ "type": "module", "scripts": { "dev": "vite", - "build": "vite build && tsc", - "standalone": "node --experimental-network-imports dist/jitar.js --config=services/standalone.json", - "repo": "node --experimental-network-imports dist/jitar.js --config=services/repository.json", - "gateway": "node --experimental-network-imports dist/jitar.js --config=services/gateway.json", - "worker": "node --experimental-network-imports dist/jitar.js --config=services/worker.json", - "proxy": "node --experimental-network-imports dist/jitar.js --config=services/proxy.json", + "build": "npm run build-domain && npm run build-webui", + "build-domain": "rm -rf dist && tsc -p tsconfig.build.json && jitar build", + "build-webui": "vite build", + "standalone": "jitar start --service=services/standalone.json", + "repo": "jitar start --service=services/repository.json", + "gateway": "jitar start --service=services/gateway.json", + "worker": "jitar start --service=services/worker.json", + "proxy": "jitar start --service=services/proxy.json", "preview": "vite preview" }, "dependencies": { diff --git a/examples/apps/full-stack/segments/client.segment.json b/examples/apps/full-stack/segments/client.segment.json new file mode 100644 index 00000000..964ebef6 --- /dev/null +++ b/examples/apps/full-stack/segments/client.segment.json @@ -0,0 +1,3 @@ +{ + "./domain/contact/Contact": { "default": { } } +} \ No newline at end of file diff --git a/examples/apps/full-stack/segments/server.segment.json b/examples/apps/full-stack/segments/server.segment.json index 2f5e4189..020ddb50 100644 --- a/examples/apps/full-stack/segments/server.segment.json +++ b/examples/apps/full-stack/segments/server.segment.json @@ -1,4 +1,5 @@ { + "./domain/contact/Contact": { "default": { } }, "./domain/contact/createContact": { "default": { "access": "public" } }, "./domain/contact/deleteContact": { "default": { "access": "public" } }, "./domain/contact/getContacts": { "default": { "access": "public" } } diff --git a/examples/apps/full-stack/services/gateway.json b/examples/apps/full-stack/services/gateway.json index 4c1dfdb1..403fcc23 100644 --- a/examples/apps/full-stack/services/gateway.json +++ b/examples/apps/full-stack/services/gateway.json @@ -1,6 +1,6 @@ { "url": "http://127.0.0.1:3000", "gateway": { - "repository": "http://127.0.0.1:2999" + } } \ No newline at end of file diff --git a/examples/apps/full-stack/services/standalone.json b/examples/apps/full-stack/services/standalone.json index 1dcfe277..d9f81ad1 100644 --- a/examples/apps/full-stack/services/standalone.json +++ b/examples/apps/full-stack/services/standalone.json @@ -3,10 +3,11 @@ "setUp": ["./integrations/jitar/setUpDatabase"], "tearDown": ["./integrations/jitar/tearDownDatabase"], "healthChecks": ["./integrations/jitar/databaseHealthCheck"], + "middleware": ["./integrations/jitar/requestLogger"], "standalone": { + "segments": ["server"], "assets": [ "index.html", "assets/**/*" ], - "middlewares": ["./integrations/jitar/requestLogger"], "serveIndexOnNotFound": true } } \ No newline at end of file diff --git a/examples/apps/full-stack/services/worker.json b/examples/apps/full-stack/services/worker.json index 0facf29f..5247ed52 100644 --- a/examples/apps/full-stack/services/worker.json +++ b/examples/apps/full-stack/services/worker.json @@ -2,12 +2,11 @@ "url": "http://127.0.0.1:3001", "setUp": ["./integrations/jitar/setUpDatabase"], "tearDown": ["./integrations/jitar/tearDownDatabase"], + "middleware": ["./integrations/jitar/requestLogger"], "healthChecks": ["./integrations/jitar/databaseHealthCheck"], "worker": { - "gateway": "http://127.0.0.1:3000", - "repository": "http://127.0.0.1:2999", "segments": [ "server" ], - "middlewares": ["./integrations/jitar/requestLogger"] + "gateway": "http://127.0.0.1:3000" } } \ No newline at end of file diff --git a/examples/apps/full-stack/src/jitar.ts b/examples/apps/full-stack/src/jitar.ts deleted file mode 100644 index 6ae887de..00000000 --- a/examples/apps/full-stack/src/jitar.ts +++ /dev/null @@ -1,9 +0,0 @@ - -import { buildServer } from 'jitar'; - -const moduleImporter = async (specifier: string) => import(specifier); -const server = await buildServer(moduleImporter); - -process.on('SIGINT', async () => server.stop()); - -server.start(); diff --git a/examples/apps/full-stack/tsconfig.base.json b/examples/apps/full-stack/tsconfig.base.json new file mode 100644 index 00000000..a3e75ece --- /dev/null +++ b/examples/apps/full-stack/tsconfig.base.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "target": "ESNext", + "useDefineForClassFields": true, + "allowJs": false, + "skipLibCheck": true, + "esModuleInterop": false, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "module": "ESNext", + "moduleResolution": "Node", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "paths": { + "^/*": ["./src/*"], + } + }, + "include": ["src", "test"] +} \ No newline at end of file diff --git a/examples/apps/full-stack/tsconfig.build.json b/examples/apps/full-stack/tsconfig.build.json new file mode 100644 index 00000000..63022935 --- /dev/null +++ b/examples/apps/full-stack/tsconfig.build.json @@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig.base.json", + "compilerOptions": { + "noEmit": false, + "jsx": "react-jsx", + "rootDir": "./src", + "outDir": "./dist", + }, + "include": ["src"], + "exclude": ["src/webui"] +} \ No newline at end of file diff --git a/examples/apps/full-stack/tsconfig.json b/examples/apps/full-stack/tsconfig.json index c42b9ef8..0ab93b49 100644 --- a/examples/apps/full-stack/tsconfig.json +++ b/examples/apps/full-stack/tsconfig.json @@ -1,23 +1,6 @@ { + "extends": "./tsconfig.base.json", "compilerOptions": { - "target": "ESNext", - "useDefineForClassFields": true, - "lib": ["DOM", "DOM.Iterable", "ESNext"], - "allowJs": false, - "skipLibCheck": true, - "esModuleInterop": false, - "allowSyntheticDefaultImports": true, - "strict": true, - "forceConsistentCasingInFileNames": true, - "module": "ESNext", - "moduleResolution": "Node", - "resolveJsonModule": true, - "isolatedModules": true, - "noEmit": false, - "outDir": "dist", "jsx": "react-jsx" - }, - "include": ["src"], - "exclude": ["src/webui"], - "references": [{ "path": "./tsconfig.node.json" }] -} + } +} \ No newline at end of file diff --git a/examples/apps/full-stack/tsconfig.node.json b/examples/apps/full-stack/tsconfig.node.json deleted file mode 100644 index 9d31e2ae..00000000 --- a/examples/apps/full-stack/tsconfig.node.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "compilerOptions": { - "composite": true, - "module": "ESNext", - "moduleResolution": "Node", - "allowSyntheticDefaultImports": true - }, - "include": ["vite.config.ts"] -} diff --git a/examples/apps/full-stack/vite.config.ts b/examples/apps/full-stack/vite.config.ts index 607945cd..cf4a1795 100644 --- a/examples/apps/full-stack/vite.config.ts +++ b/examples/apps/full-stack/vite.config.ts @@ -3,8 +3,11 @@ import react from '@vitejs/plugin-react'; import jitar from '@jitar/plugin-vite'; export default defineConfig({ + build: { + emptyOutDir: false + }, plugins: [ react(), - jitar('src', 'domain', 'http://localhost:3000') + jitar({ sourceDir: 'src', targetDir: 'dist', jitarDir: 'domain', jitarUrl: 'http://localhost:3000', segments: ['client'] }) ] }) diff --git a/examples/concepts/access-protection/jitar.json b/examples/concepts/access-protection/jitar.json new file mode 100644 index 00000000..4bb49481 --- /dev/null +++ b/examples/concepts/access-protection/jitar.json @@ -0,0 +1,4 @@ +{ + "source": "./dist", + "target": "./.jitar" +} \ No newline at end of file diff --git a/examples/concepts/access-protection/package.json b/examples/concepts/access-protection/package.json index 429b6e67..42f5d12f 100644 --- a/examples/concepts/access-protection/package.json +++ b/examples/concepts/access-protection/package.json @@ -3,12 +3,11 @@ "type": "module", "private": true, "scripts": { - "build": "tsc", - "standalone": "node --experimental-network-imports --env-file=.env dist/jitar.js --config=services/standalone.json", - "repo": "node --experimental-network-imports dist/jitar.js --config=services/repository.json", - "gateway": "node --experimental-network-imports --env-file=.env dist/jitar.js --config=services/gateway.json", - "worker-web": "node --experimental-network-imports --env-file=.env dist/jitar.js --config=services/web.json", - "worker-game": "node --experimental-network-imports --env-file=.env dist/jitar.js --config=services/game.json" + "build": "rm -rf dist .jitar && tsc && jitar build", + "standalone": "jitar start --service=services/standalone.json", + "gateway": "jitar start --service=services/gateway.json", + "worker-public": "jitar start --service=services/public.json", + "worker-protected": "jitar start --service=services/protected.json" }, "dependencies": { "jitar": "*" diff --git a/examples/concepts/access-protection/services/gateway.json b/examples/concepts/access-protection/services/gateway.json index 76118495..a6997c2b 100644 --- a/examples/concepts/access-protection/services/gateway.json +++ b/examples/concepts/access-protection/services/gateway.json @@ -1,7 +1,6 @@ { "url": "http://127.0.0.1:3000", "gateway": { - "repository": "http://127.0.0.1:2999", "trustKey": "${TRUST_KEY}" } } \ No newline at end of file diff --git a/examples/concepts/access-protection/services/game.json b/examples/concepts/access-protection/services/protected.json similarity index 79% rename from examples/concepts/access-protection/services/game.json rename to examples/concepts/access-protection/services/protected.json index eb417ea0..78433229 100644 --- a/examples/concepts/access-protection/services/game.json +++ b/examples/concepts/access-protection/services/protected.json @@ -3,7 +3,6 @@ "worker": { "gateway": "http://127.0.0.1:3000", - "repository": "http://127.0.0.1:2999", "segments": [ "protected" ], "trustKey": "${TRUST_KEY}" } diff --git a/examples/concepts/access-protection/services/web.json b/examples/concepts/access-protection/services/public.json similarity index 79% rename from examples/concepts/access-protection/services/web.json rename to examples/concepts/access-protection/services/public.json index 032ce3f5..f0362935 100644 --- a/examples/concepts/access-protection/services/web.json +++ b/examples/concepts/access-protection/services/public.json @@ -3,7 +3,6 @@ "worker": { "gateway": "http://127.0.0.1:3000", - "repository": "http://127.0.0.1:2999", "segments": [ "public" ], "trustKey": "${TRUST_KEY}" } diff --git a/examples/concepts/access-protection/services/repository.json b/examples/concepts/access-protection/services/repository.json deleted file mode 100644 index d60c52d8..00000000 --- a/examples/concepts/access-protection/services/repository.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "url": "http://127.0.0.1:2999", - "repository": - { - - } -} \ No newline at end of file diff --git a/examples/concepts/access-protection/services/standalone.json b/examples/concepts/access-protection/services/standalone.json index 85564881..410af37e 100644 --- a/examples/concepts/access-protection/services/standalone.json +++ b/examples/concepts/access-protection/services/standalone.json @@ -1,7 +1,8 @@ { "url": "http://127.0.0.1:3000", - "standalone": + "worker": { + "segments": ["public", "protected"], "trustKey": "${TRUST_KEY}" } } \ No newline at end of file diff --git a/examples/concepts/access-protection/src/jitar.ts b/examples/concepts/access-protection/src/jitar.ts deleted file mode 100644 index 6ae887de..00000000 --- a/examples/concepts/access-protection/src/jitar.ts +++ /dev/null @@ -1,9 +0,0 @@ - -import { buildServer } from 'jitar'; - -const moduleImporter = async (specifier: string) => import(specifier); -const server = await buildServer(moduleImporter); - -process.on('SIGINT', async () => server.stop()); - -server.start(); diff --git a/examples/concepts/construction/jitar.json b/examples/concepts/construction/jitar.json new file mode 100644 index 00000000..4bb49481 --- /dev/null +++ b/examples/concepts/construction/jitar.json @@ -0,0 +1,4 @@ +{ + "source": "./dist", + "target": "./.jitar" +} \ No newline at end of file diff --git a/examples/concepts/construction/package.json b/examples/concepts/construction/package.json index 596aed1c..bdb1a7a7 100644 --- a/examples/concepts/construction/package.json +++ b/examples/concepts/construction/package.json @@ -3,8 +3,8 @@ "private": true, "type": "module", "scripts": { - "build": "tsc", - "standalone": "node --experimental-network-imports dist/jitar.js --config=services/standalone.json" + "build": "rm -rf dist .jitar && tsc && jitar build", + "standalone": "jitar start --service=services/standalone.json" }, "dependencies": { "jitar": "*" diff --git a/examples/concepts/construction/services/standalone.json b/examples/concepts/construction/services/standalone.json index eddd1ad6..3d857baa 100644 --- a/examples/concepts/construction/services/standalone.json +++ b/examples/concepts/construction/services/standalone.json @@ -2,5 +2,7 @@ "url": "http://127.0.0.1:3000", "setUp": ["./setUpDatabase"], "tearDown": ["./tearDownDatabase"], - "standalone": {} + "worker": { + "segments": ["default"] + } } \ No newline at end of file diff --git a/examples/concepts/construction/src/jitar.ts b/examples/concepts/construction/src/jitar.ts deleted file mode 100644 index 6ae887de..00000000 --- a/examples/concepts/construction/src/jitar.ts +++ /dev/null @@ -1,9 +0,0 @@ - -import { buildServer } from 'jitar'; - -const moduleImporter = async (specifier: string) => import(specifier); -const server = await buildServer(moduleImporter); - -process.on('SIGINT', async () => server.stop()); - -server.start(); diff --git a/examples/concepts/cors/jitar.json b/examples/concepts/cors/jitar.json new file mode 100644 index 00000000..4bb49481 --- /dev/null +++ b/examples/concepts/cors/jitar.json @@ -0,0 +1,4 @@ +{ + "source": "./dist", + "target": "./.jitar" +} \ No newline at end of file diff --git a/examples/concepts/cors/package.json b/examples/concepts/cors/package.json index 825c42bb..6ebd1566 100644 --- a/examples/concepts/cors/package.json +++ b/examples/concepts/cors/package.json @@ -3,11 +3,11 @@ "type": "module", "private": true, "scripts": { - "clean": "rimraf dist", - "compile": "tsc", + "clean": "rm -rf dist .jitar", + "compile": "tsc && jitar build", "copy": "cpx -u 'src/index.html' dist", - "build": "npm-run-all clean copy compile", - "standalone": "node --experimental-network-imports dist/jitar.js --config=services/standalone.json", + "build": "npm run clean && npm run copy && npm run compile", + "standalone": "jitar start --service=services/standalone.json", "client": "node dist/client.js" }, "dependencies": { @@ -15,8 +15,6 @@ "express": "*" }, "devDependencies": { - "cpx2": "*", - "npm-run-all": "*", - "rimraf": "*" + "cpx2": "*" } } \ No newline at end of file diff --git a/examples/concepts/cors/requests.http b/examples/concepts/cors/requests.http new file mode 100644 index 00000000..cfc00c42 --- /dev/null +++ b/examples/concepts/cors/requests.http @@ -0,0 +1,2 @@ + +GET http://localhost:3000/rpc/getWeatherForecast HTTP/1.1 diff --git a/examples/concepts/cors/services/standalone.json b/examples/concepts/cors/services/standalone.json index b83d6401..e13071ec 100644 --- a/examples/concepts/cors/services/standalone.json +++ b/examples/concepts/cors/services/standalone.json @@ -1,7 +1,8 @@ { "url": "http://127.0.0.1:3000", - "standalone": + "middleware": ["./handleCors"], + "worker": { - "middlewares": ["./handleCors"] + "segments": ["server"] } } \ No newline at end of file diff --git a/examples/concepts/cors/src/jitar.ts b/examples/concepts/cors/src/jitar.ts deleted file mode 100644 index 6ae887de..00000000 --- a/examples/concepts/cors/src/jitar.ts +++ /dev/null @@ -1,9 +0,0 @@ - -import { buildServer } from 'jitar'; - -const moduleImporter = async (specifier: string) => import(specifier); -const server = await buildServer(moduleImporter); - -process.on('SIGINT', async () => server.stop()); - -server.start(); diff --git a/examples/concepts/data-transportation/jitar.json b/examples/concepts/data-transportation/jitar.json new file mode 100644 index 00000000..4bb49481 --- /dev/null +++ b/examples/concepts/data-transportation/jitar.json @@ -0,0 +1,4 @@ +{ + "source": "./dist", + "target": "./.jitar" +} \ No newline at end of file diff --git a/examples/concepts/data-transportation/package.json b/examples/concepts/data-transportation/package.json index 51702277..48163a60 100644 --- a/examples/concepts/data-transportation/package.json +++ b/examples/concepts/data-transportation/package.json @@ -3,12 +3,11 @@ "type": "module", "private": true, "scripts": { - "build": "tsc", - "standalone": "node --experimental-network-imports dist/jitar.js --config=services/standalone.json", - "repo": "node --experimental-network-imports dist/jitar.js --config=services/repository.json", - "gateway": "node --experimental-network-imports dist/jitar.js --config=services/gateway.json", - "worker-account": "node --experimental-network-imports dist/jitar.js --config=services/account.json", - "worker-helpdesk": "node --experimental-network-imports dist/jitar.js --config=services/helpdesk.json" + "build": "rm -rf dist .jitar && tsc && jitar build", + "standalone": "jitar start --service=services/standalone.json", + "gateway": "jitar start --service=services/gateway.json", + "worker-account": "jitar start --service=services/account.json", + "worker-helpdesk": "jitar start --service=services/helpdesk.json" }, "dependencies": { "jitar": "*" diff --git a/examples/concepts/data-transportation/requests.http b/examples/concepts/data-transportation/requests.http index 5eb51314..bb5be4d1 100644 --- a/examples/concepts/data-transportation/requests.http +++ b/examples/concepts/data-transportation/requests.http @@ -4,9 +4,18 @@ GET http://localhost:3000/rpc/helpdesk/register?firstName=John&lastName=Doe HTTP/1.1 +### + +// Create an account for John Doe +// This will return the non-serialized account object + +GET http://localhost:3000/rpc/account/createAccount?firstName=John&lastName=Doe HTTP/1.1 + + ### // Create an account for John Doe // This will return the serialized account object -GET http://localhost:3000/rpc/account/createAccount?firstName=John&lastName=Doe&serialize=true HTTP/1.1 +GET http://localhost:3000/rpc/account/createAccount?firstName=John&lastName=Doe HTTP/1.1 +X-Jitar-Data-Encoding: serialized diff --git a/examples/concepts/data-transportation/segments/account.segment.json b/examples/concepts/data-transportation/segments/account.segment.json index e14b94a1..5bc012db 100644 --- a/examples/concepts/data-transportation/segments/account.segment.json +++ b/examples/concepts/data-transportation/segments/account.segment.json @@ -4,5 +4,8 @@ "access": "public", "version": "0.0.0" } + }, + "./account/Account": { + "default": { } } } \ No newline at end of file diff --git a/examples/concepts/data-transportation/segments/helpdesk.segment.json b/examples/concepts/data-transportation/segments/helpdesk.segment.json index b0b5ad2e..8e9560c3 100644 --- a/examples/concepts/data-transportation/segments/helpdesk.segment.json +++ b/examples/concepts/data-transportation/segments/helpdesk.segment.json @@ -4,5 +4,8 @@ "access": "public", "version": "0.0.0" } + }, + "./account/Account": { + "default": { } } } \ No newline at end of file diff --git a/examples/concepts/data-transportation/services/account.json b/examples/concepts/data-transportation/services/account.json index a3b24553..016589d7 100644 --- a/examples/concepts/data-transportation/services/account.json +++ b/examples/concepts/data-transportation/services/account.json @@ -3,7 +3,6 @@ "worker": { "gateway": "http://127.0.0.1:3000", - "repository": "http://127.0.0.1:2999", "segments": [ "account" ] } } \ No newline at end of file diff --git a/examples/concepts/data-transportation/services/gateway.json b/examples/concepts/data-transportation/services/gateway.json index 4c1dfdb1..2a47b940 100644 --- a/examples/concepts/data-transportation/services/gateway.json +++ b/examples/concepts/data-transportation/services/gateway.json @@ -1,6 +1,4 @@ { "url": "http://127.0.0.1:3000", - "gateway": { - "repository": "http://127.0.0.1:2999" - } + "gateway": { } } \ No newline at end of file diff --git a/examples/concepts/data-transportation/services/helpdesk.json b/examples/concepts/data-transportation/services/helpdesk.json index 000b9266..f9242741 100644 --- a/examples/concepts/data-transportation/services/helpdesk.json +++ b/examples/concepts/data-transportation/services/helpdesk.json @@ -3,7 +3,6 @@ "worker": { "gateway": "http://127.0.0.1:3000", - "repository": "http://127.0.0.1:2999", "segments": [ "helpdesk" ] } } \ No newline at end of file diff --git a/examples/concepts/data-transportation/services/repository.json b/examples/concepts/data-transportation/services/repository.json deleted file mode 100644 index d60c52d8..00000000 --- a/examples/concepts/data-transportation/services/repository.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "url": "http://127.0.0.1:2999", - "repository": - { - - } -} \ No newline at end of file diff --git a/examples/concepts/data-transportation/services/standalone.json b/examples/concepts/data-transportation/services/standalone.json index 9117bcfa..a62a0c34 100644 --- a/examples/concepts/data-transportation/services/standalone.json +++ b/examples/concepts/data-transportation/services/standalone.json @@ -1,7 +1,7 @@ { "url": "http://127.0.0.1:3000", - "standalone": + "worker": { - + "segments": ["account", "helpdesk"] } } \ No newline at end of file diff --git a/examples/concepts/data-transportation/src/jitar.ts b/examples/concepts/data-transportation/src/jitar.ts deleted file mode 100644 index 6ae887de..00000000 --- a/examples/concepts/data-transportation/src/jitar.ts +++ /dev/null @@ -1,9 +0,0 @@ - -import { buildServer } from 'jitar'; - -const moduleImporter = async (specifier: string) => import(specifier); -const server = await buildServer(moduleImporter); - -process.on('SIGINT', async () => server.stop()); - -server.start(); diff --git a/examples/concepts/error-handling/jitar.json b/examples/concepts/error-handling/jitar.json new file mode 100644 index 00000000..4bb49481 --- /dev/null +++ b/examples/concepts/error-handling/jitar.json @@ -0,0 +1,4 @@ +{ + "source": "./dist", + "target": "./.jitar" +} \ No newline at end of file diff --git a/examples/concepts/error-handling/package.json b/examples/concepts/error-handling/package.json index 478c82da..19a4c517 100644 --- a/examples/concepts/error-handling/package.json +++ b/examples/concepts/error-handling/package.json @@ -3,12 +3,11 @@ "type": "module", "private": true, "scripts": { - "build": "tsc", - "standalone": "node --experimental-network-imports dist/jitar.js --config=services/standalone.json", - "repo": "node --experimental-network-imports dist/jitar.js --config=services/repository.json", - "gateway": "node --experimental-network-imports dist/jitar.js --config=services/gateway.json", - "worker-data": "node --experimental-network-imports dist/jitar.js --config=services/data.json", - "worker-process": "node --experimental-network-imports dist/jitar.js --config=services/process.json" + "build": "rm -rf dist .jitar && tsc && jitar build", + "standalone": "jitar start --service=services/standalone.json", + "gateway": "jitar start --service=services/gateway.json", + "worker-data": "jitar start --service=services/data.json", + "worker-process": "jitar start --service=services/process.json" }, "dependencies": { "jitar": "*" diff --git a/examples/concepts/error-handling/requests.http b/examples/concepts/error-handling/requests.http index f9ac20d2..0cf9216e 100644 --- a/examples/concepts/error-handling/requests.http +++ b/examples/concepts/error-handling/requests.http @@ -1,10 +1,10 @@ // The database error is caught and printed to the console. -GET http://localhost:3000/rpc/sales/getData HTTP/1.1 +GET http://localhost:3000/rpc/getData HTTP/1.1 ### // The database error is uncaught and send as reponse. -GET http://localhost:3000/rpc/sales/exportData HTTP/1.1 +GET http://localhost:3000/rpc/exportData HTTP/1.1 diff --git a/examples/concepts/error-handling/segments/data.segment.json b/examples/concepts/error-handling/segments/data.segment.json index 234e225a..5e4fcc66 100644 --- a/examples/concepts/error-handling/segments/data.segment.json +++ b/examples/concepts/error-handling/segments/data.segment.json @@ -1,5 +1,6 @@ { - "./sales/getData": { + "./DatabaseError": { "default": {} }, + "./getData": { "default": { "access": "public", "version": "0.0.0" diff --git a/examples/concepts/error-handling/segments/process.segment.json b/examples/concepts/error-handling/segments/process.segment.json index cba9d2c8..669b4459 100644 --- a/examples/concepts/error-handling/segments/process.segment.json +++ b/examples/concepts/error-handling/segments/process.segment.json @@ -1,5 +1,6 @@ { - "./sales/exportData": { + "./DatabaseError": { "default": {} }, + "./exportData": { "default": { "access": "public", "version": "0.0.0" diff --git a/examples/concepts/error-handling/services/data.json b/examples/concepts/error-handling/services/data.json index ba463f83..68008d4b 100644 --- a/examples/concepts/error-handling/services/data.json +++ b/examples/concepts/error-handling/services/data.json @@ -3,7 +3,6 @@ "worker": { "gateway": "http://127.0.0.1:3000", - "repository": "http://127.0.0.1:2999", "segments": [ "data" ] } } \ No newline at end of file diff --git a/examples/concepts/error-handling/services/gateway.json b/examples/concepts/error-handling/services/gateway.json index 4c1dfdb1..2a47b940 100644 --- a/examples/concepts/error-handling/services/gateway.json +++ b/examples/concepts/error-handling/services/gateway.json @@ -1,6 +1,4 @@ { "url": "http://127.0.0.1:3000", - "gateway": { - "repository": "http://127.0.0.1:2999" - } + "gateway": { } } \ No newline at end of file diff --git a/examples/concepts/error-handling/services/process.json b/examples/concepts/error-handling/services/process.json index ee772123..b069be88 100644 --- a/examples/concepts/error-handling/services/process.json +++ b/examples/concepts/error-handling/services/process.json @@ -3,7 +3,6 @@ "worker": { "gateway": "http://127.0.0.1:3000", - "repository": "http://127.0.0.1:2999", "segments": [ "process" ] } } \ No newline at end of file diff --git a/examples/concepts/error-handling/services/repository.json b/examples/concepts/error-handling/services/repository.json deleted file mode 100644 index d60c52d8..00000000 --- a/examples/concepts/error-handling/services/repository.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "url": "http://127.0.0.1:2999", - "repository": - { - - } -} \ No newline at end of file diff --git a/examples/concepts/error-handling/services/standalone.json b/examples/concepts/error-handling/services/standalone.json index 9117bcfa..94daa361 100644 --- a/examples/concepts/error-handling/services/standalone.json +++ b/examples/concepts/error-handling/services/standalone.json @@ -1,7 +1,7 @@ { "url": "http://127.0.0.1:3000", - "standalone": + "worker": { - + "segments": [ "data", "process" ] } } \ No newline at end of file diff --git a/examples/concepts/error-handling/src/sales/DatabaseError.ts b/examples/concepts/error-handling/src/DatabaseError.ts similarity index 100% rename from examples/concepts/error-handling/src/sales/DatabaseError.ts rename to examples/concepts/error-handling/src/DatabaseError.ts diff --git a/examples/concepts/error-handling/src/sales/exportData.ts b/examples/concepts/error-handling/src/exportData.ts similarity index 100% rename from examples/concepts/error-handling/src/sales/exportData.ts rename to examples/concepts/error-handling/src/exportData.ts diff --git a/examples/concepts/error-handling/src/sales/getData.ts b/examples/concepts/error-handling/src/getData.ts similarity index 100% rename from examples/concepts/error-handling/src/sales/getData.ts rename to examples/concepts/error-handling/src/getData.ts diff --git a/examples/concepts/error-handling/src/jitar.ts b/examples/concepts/error-handling/src/jitar.ts deleted file mode 100644 index 6ae887de..00000000 --- a/examples/concepts/error-handling/src/jitar.ts +++ /dev/null @@ -1,9 +0,0 @@ - -import { buildServer } from 'jitar'; - -const moduleImporter = async (specifier: string) => import(specifier); -const server = await buildServer(moduleImporter); - -process.on('SIGINT', async () => server.stop()); - -server.start(); diff --git a/examples/concepts/health-checks/jitar.json b/examples/concepts/health-checks/jitar.json new file mode 100644 index 00000000..4bb49481 --- /dev/null +++ b/examples/concepts/health-checks/jitar.json @@ -0,0 +1,4 @@ +{ + "source": "./dist", + "target": "./.jitar" +} \ No newline at end of file diff --git a/examples/concepts/health-checks/package.json b/examples/concepts/health-checks/package.json index 8544b256..9663ce9e 100644 --- a/examples/concepts/health-checks/package.json +++ b/examples/concepts/health-checks/package.json @@ -3,8 +3,8 @@ "type": "module", "private": true, "scripts": { - "build": "tsc", - "standalone": "node --experimental-network-imports dist/jitar.js --config=services/standalone.json" + "build": "rm -rf dist .jitar && tsc && jitar build", + "standalone": "jitar start --service=services/standalone.json" }, "dependencies": { "jitar": "*" diff --git a/examples/concepts/health-checks/services/standalone.json b/examples/concepts/health-checks/services/standalone.json index b541842b..0d5bad04 100644 --- a/examples/concepts/health-checks/services/standalone.json +++ b/examples/concepts/health-checks/services/standalone.json @@ -1,8 +1,8 @@ { "url": "http://127.0.0.1:3000", "healthChecks": ["./databaseHealthCheck"], - "standalone": + "worker": { - + "segments": ["default"] } } \ No newline at end of file diff --git a/examples/concepts/health-checks/src/jitar.ts b/examples/concepts/health-checks/src/jitar.ts deleted file mode 100644 index 6ae887de..00000000 --- a/examples/concepts/health-checks/src/jitar.ts +++ /dev/null @@ -1,9 +0,0 @@ - -import { buildServer } from 'jitar'; - -const moduleImporter = async (specifier: string) => import(specifier); -const server = await buildServer(moduleImporter); - -process.on('SIGINT', async () => server.stop()); - -server.start(); diff --git a/examples/concepts/hello-world/jitar.json b/examples/concepts/hello-world/jitar.json new file mode 100644 index 00000000..4bb49481 --- /dev/null +++ b/examples/concepts/hello-world/jitar.json @@ -0,0 +1,4 @@ +{ + "source": "./dist", + "target": "./.jitar" +} \ No newline at end of file diff --git a/examples/concepts/hello-world/package.json b/examples/concepts/hello-world/package.json index 5549043e..b4e640c7 100644 --- a/examples/concepts/hello-world/package.json +++ b/examples/concepts/hello-world/package.json @@ -3,8 +3,8 @@ "private": true, "type": "module", "scripts": { - "build": "tsc", - "standalone": "node --experimental-network-imports dist/jitar.js --config=services/standalone.json" + "build": "rm -rf dist .jitar && tsc && jitar build", + "standalone": "jitar start --service=services/standalone.json" }, "dependencies": { "jitar": "*" diff --git a/examples/concepts/hello-world/services/standalone.json b/examples/concepts/hello-world/services/standalone.json index 9117bcfa..b77db7d6 100644 --- a/examples/concepts/hello-world/services/standalone.json +++ b/examples/concepts/hello-world/services/standalone.json @@ -1,7 +1,7 @@ { "url": "http://127.0.0.1:3000", - "standalone": + "worker": { - + "segments": ["default"] } } \ No newline at end of file diff --git a/examples/concepts/hello-world/src/jitar.ts b/examples/concepts/hello-world/src/jitar.ts deleted file mode 100644 index 6ae887de..00000000 --- a/examples/concepts/hello-world/src/jitar.ts +++ /dev/null @@ -1,9 +0,0 @@ - -import { buildServer } from 'jitar'; - -const moduleImporter = async (specifier: string) => import(specifier); -const server = await buildServer(moduleImporter); - -process.on('SIGINT', async () => server.stop()); - -server.start(); diff --git a/examples/concepts/load-balancing/jitar.json b/examples/concepts/load-balancing/jitar.json new file mode 100644 index 00000000..4bb49481 --- /dev/null +++ b/examples/concepts/load-balancing/jitar.json @@ -0,0 +1,4 @@ +{ + "source": "./dist", + "target": "./.jitar" +} \ No newline at end of file diff --git a/examples/concepts/load-balancing/package.json b/examples/concepts/load-balancing/package.json index 5c5834a7..69d1a849 100644 --- a/examples/concepts/load-balancing/package.json +++ b/examples/concepts/load-balancing/package.json @@ -3,12 +3,11 @@ "type": "module", "private": true, "scripts": { - "build": "tsc", - "standalone": "node --experimental-network-imports dist/jitar.js --config=services/standalone.json", - "repo": "node --experimental-network-imports dist/jitar.js --config=services/repository.json", - "gateway": "node --experimental-network-imports dist/jitar.js --config=services/gateway.json", - "worker1": "node --experimental-network-imports dist/jitar.js --config=services/worker1.json", - "worker2": "node --experimental-network-imports dist/jitar.js --config=services/worker2.json" + "build": "rm -rf dist .jitar && tsc && jitar build", + "standalone": "jitar start --service=services/standalone.json", + "gateway": "jitar start --service=services/gateway.json", + "worker1": "jitar start --service=services/worker1.json", + "worker2": "jitar start --service=services/worker2.json" }, "dependencies": { "jitar": "*" diff --git a/examples/concepts/load-balancing/requests.http b/examples/concepts/load-balancing/requests.http index dcb0b6f3..4f196a35 100644 --- a/examples/concepts/load-balancing/requests.http +++ b/examples/concepts/load-balancing/requests.http @@ -17,7 +17,7 @@ POST http://localhost:3000/rpc/calculator/subtract HTTP/1.1 content-type: application/json { - "first": 48, + "first": 12, "second": 6 } diff --git a/examples/concepts/load-balancing/services/gateway.json b/examples/concepts/load-balancing/services/gateway.json index 4c1dfdb1..2a47b940 100644 --- a/examples/concepts/load-balancing/services/gateway.json +++ b/examples/concepts/load-balancing/services/gateway.json @@ -1,6 +1,4 @@ { "url": "http://127.0.0.1:3000", - "gateway": { - "repository": "http://127.0.0.1:2999" - } + "gateway": { } } \ No newline at end of file diff --git a/examples/concepts/load-balancing/services/repository.json b/examples/concepts/load-balancing/services/repository.json deleted file mode 100644 index d60c52d8..00000000 --- a/examples/concepts/load-balancing/services/repository.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "url": "http://127.0.0.1:2999", - "repository": - { - - } -} \ No newline at end of file diff --git a/examples/concepts/load-balancing/services/standalone.json b/examples/concepts/load-balancing/services/standalone.json index 9117bcfa..7f2ba8ad 100644 --- a/examples/concepts/load-balancing/services/standalone.json +++ b/examples/concepts/load-balancing/services/standalone.json @@ -1,7 +1,7 @@ { "url": "http://127.0.0.1:3000", - "standalone": + "worker": { - + "segments": ["calculator"] } } \ No newline at end of file diff --git a/examples/concepts/load-balancing/services/worker1.json b/examples/concepts/load-balancing/services/worker1.json index 470a3175..ccc2f6ea 100644 --- a/examples/concepts/load-balancing/services/worker1.json +++ b/examples/concepts/load-balancing/services/worker1.json @@ -3,7 +3,6 @@ "worker": { "gateway": "http://127.0.0.1:3000", - "repository": "http://127.0.0.1:2999", "segments": [ "calculator" ] } } \ No newline at end of file diff --git a/examples/concepts/load-balancing/services/worker2.json b/examples/concepts/load-balancing/services/worker2.json index a24aadb1..c3ce127f 100644 --- a/examples/concepts/load-balancing/services/worker2.json +++ b/examples/concepts/load-balancing/services/worker2.json @@ -3,7 +3,6 @@ "worker": { "gateway": "http://127.0.0.1:3000", - "repository": "http://127.0.0.1:2999", "segments": [ "calculator" ] } } \ No newline at end of file diff --git a/examples/concepts/load-balancing/src/jitar.ts b/examples/concepts/load-balancing/src/jitar.ts deleted file mode 100644 index 6ae887de..00000000 --- a/examples/concepts/load-balancing/src/jitar.ts +++ /dev/null @@ -1,9 +0,0 @@ - -import { buildServer } from 'jitar'; - -const moduleImporter = async (specifier: string) => import(specifier); -const server = await buildServer(moduleImporter); - -process.on('SIGINT', async () => server.stop()); - -server.start(); diff --git a/examples/concepts/middleware/jitar.json b/examples/concepts/middleware/jitar.json new file mode 100644 index 00000000..4bb49481 --- /dev/null +++ b/examples/concepts/middleware/jitar.json @@ -0,0 +1,4 @@ +{ + "source": "./dist", + "target": "./.jitar" +} \ No newline at end of file diff --git a/examples/concepts/middleware/package.json b/examples/concepts/middleware/package.json index 0cb1d983..c4e2d621 100644 --- a/examples/concepts/middleware/package.json +++ b/examples/concepts/middleware/package.json @@ -3,8 +3,8 @@ "type": "module", "private": true, "scripts": { - "build": "tsc", - "standalone": "node --experimental-network-imports dist/jitar.js --config=services/standalone.json" + "build": "rm -rf dist .jitar && tsc && jitar build", + "standalone": "jitar start --service=services/standalone.json" }, "dependencies": { "jitar": "*" diff --git a/examples/concepts/middleware/services/standalone.json b/examples/concepts/middleware/services/standalone.json index 8d7875f5..e1355036 100644 --- a/examples/concepts/middleware/services/standalone.json +++ b/examples/concepts/middleware/services/standalone.json @@ -1,7 +1,12 @@ { "url": "http://127.0.0.1:3000", - "standalone": + "middleware": [ + "./loggingMiddleware", + "./redirectMiddleware", + "./argumentMiddleware" + ], + "worker": { - "middlewares": ["./loggingMiddleware", "./redirectMiddleware", "./argumentMiddleware"] + "segments": ["default"] } } \ No newline at end of file diff --git a/examples/concepts/middleware/src/jitar.ts b/examples/concepts/middleware/src/jitar.ts deleted file mode 100644 index 6ae887de..00000000 --- a/examples/concepts/middleware/src/jitar.ts +++ /dev/null @@ -1,9 +0,0 @@ - -import { buildServer } from 'jitar'; - -const moduleImporter = async (specifier: string) => import(specifier); -const server = await buildServer(moduleImporter); - -process.on('SIGINT', async () => server.stop()); - -server.start(); diff --git a/examples/concepts/multi-version/jitar.json b/examples/concepts/multi-version/jitar.json new file mode 100644 index 00000000..4bb49481 --- /dev/null +++ b/examples/concepts/multi-version/jitar.json @@ -0,0 +1,4 @@ +{ + "source": "./dist", + "target": "./.jitar" +} \ No newline at end of file diff --git a/examples/concepts/multi-version/package.json b/examples/concepts/multi-version/package.json index a41b44d6..78475c96 100644 --- a/examples/concepts/multi-version/package.json +++ b/examples/concepts/multi-version/package.json @@ -3,8 +3,8 @@ "type": "module", "private": true, "scripts": { - "build": "tsc", - "standalone": "node --experimental-network-imports dist/jitar.js --config=services/standalone.json" + "build": "rm -rf dist .jitar && tsc && jitar build", + "standalone": "jitar start --service=services/standalone.json" }, "dependencies": { "jitar": "*" diff --git a/examples/concepts/multi-version/requests.http b/examples/concepts/multi-version/requests.http index dfc10866..b57b7df5 100644 --- a/examples/concepts/multi-version/requests.http +++ b/examples/concepts/multi-version/requests.http @@ -1,10 +1,13 @@ // Get the employee with version 1.0.0 -GET http://localhost:3000/rpc/getEmployee?version=1.0.0&id=1 HTTP/1.1 +GET http://localhost:3000/rpc/getEmployee?id=1 HTTP/1.1 +X-Jitar-Procedure-Version: 1.0.0 ### // Get the employee with version 2.0.0 -GET http://localhost:3000/rpc/getEmployee?version=2.0.0&uuid=f507cf82-ca51-4a42-8c20-ce5c7c42a77e HTTP/1.1 +GET http://localhost:3000/rpc/getEmployee?uuid=f507cf82-ca51-4a42-8c20-ce5c7c42a77e HTTP/1.1 +X-Jitar-Procedure-Version: 2.0.0 +X-Jitar-Trust-Key: MY_VERY_SECRET_KEY diff --git a/examples/concepts/multi-version/segments/default.segment.json b/examples/concepts/multi-version/segments/default.segment.json index 103b5839..30bd2eac 100644 --- a/examples/concepts/multi-version/segments/default.segment.json +++ b/examples/concepts/multi-version/segments/default.segment.json @@ -6,7 +6,7 @@ }, "getEmployeeV2": { "as": "getEmployee", - "access": "public", + "access": "protected", "version": "2.0.0" } } diff --git a/examples/concepts/multi-version/services/standalone.json b/examples/concepts/multi-version/services/standalone.json index 9117bcfa..84b401ce 100644 --- a/examples/concepts/multi-version/services/standalone.json +++ b/examples/concepts/multi-version/services/standalone.json @@ -1,7 +1,8 @@ { "url": "http://127.0.0.1:3000", - "standalone": + "worker": { - + "segments": ["default"], + "trustKey": "MY_VERY_SECRET_KEY" } } \ No newline at end of file diff --git a/examples/concepts/multi-version/src/jitar.ts b/examples/concepts/multi-version/src/jitar.ts deleted file mode 100644 index 6ae887de..00000000 --- a/examples/concepts/multi-version/src/jitar.ts +++ /dev/null @@ -1,9 +0,0 @@ - -import { buildServer } from 'jitar'; - -const moduleImporter = async (specifier: string) => import(specifier); -const server = await buildServer(moduleImporter); - -process.on('SIGINT', async () => server.stop()); - -server.start(); diff --git a/examples/concepts/node-client/README.md b/examples/concepts/node-client/README.md deleted file mode 100644 index a8ab5ef3..00000000 --- a/examples/concepts/node-client/README.md +++ /dev/null @@ -1,51 +0,0 @@ - -# Jitar | Start Hooks example - -This example demonstrates how to set up a node client. - -The application draws a random lucky number. -The client connects to the Jitar server and gets a number. - -## Project setup - -**Functions** - -* getLuckyNumber (`src/getLuckyNumber.ts`) - -**Segments** - -* Client - contains the *client* procedures (`segments/client.segment.json`) - -**Services** - -* Standalone - loads no segments (`services/standalone.json`) - -## Running the example - -1\. Install Jitar by running the following command from the root directory of the example. - -```bash -npm install -``` - -2\. Next, build the application by running the following command. - -```bash -npm run build -``` - -To start Jitar we need two terminal sessions to start the server and the node client. The starting order is of importance. - -**Standalone** (terminal 1) - -```bash -npm run standalone -``` - -**Node client** (terminal 2) - -```bash -npm run client -``` - -The lucky number is printed to the console. diff --git a/examples/concepts/node-client/package.json b/examples/concepts/node-client/package.json deleted file mode 100644 index 28731347..00000000 --- a/examples/concepts/node-client/package.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "name": "jitar-node-client-example", - "type": "module", - "private": true, - "scripts": { - "build": "tsc", - "standalone": "node --experimental-network-imports dist/jitar.js --config=services/standalone.json", - "client": "node --experimental-network-imports --no-warnings dist/client.js" - }, - "dependencies": { - "jitar": "*" - } -} \ No newline at end of file diff --git a/examples/concepts/node-client/segments/client.segment.json b/examples/concepts/node-client/segments/client.segment.json deleted file mode 100644 index 076cbd6e..00000000 --- a/examples/concepts/node-client/segments/client.segment.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "./getLuckyNumber": { - "default": { - "access": "public", - "version": "0.0.0" - } - } -} \ No newline at end of file diff --git a/examples/concepts/node-client/services/standalone.json b/examples/concepts/node-client/services/standalone.json deleted file mode 100644 index 8083107f..00000000 --- a/examples/concepts/node-client/services/standalone.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "url": "http://127.0.0.1:3000", - "standalone": - { - "segments": [ ] - } -} \ No newline at end of file diff --git a/examples/concepts/node-client/src/client.ts b/examples/concepts/node-client/src/client.ts deleted file mode 100644 index f203a17b..00000000 --- a/examples/concepts/node-client/src/client.ts +++ /dev/null @@ -1,10 +0,0 @@ - -import { startClient } from 'jitar'; - -const client = await startClient('http://127.0.0.1:3000', ['client']); - -const { default: getLuckyNumber } = await client.import('./getLuckyNumber', 'application') as any; - -const number = await getLuckyNumber(0, 100); - -console.log(`Your lucky number is ${number}!`); diff --git a/examples/concepts/node-client/src/getLuckyNumber.ts b/examples/concepts/node-client/src/getLuckyNumber.ts deleted file mode 100644 index bc5b239b..00000000 --- a/examples/concepts/node-client/src/getLuckyNumber.ts +++ /dev/null @@ -1,5 +0,0 @@ - -export default async function getLuckyNumber(min: number, max: number): Promise -{ - return Math.round(Math.random() * (max - min) + min); -} diff --git a/examples/concepts/node-client/src/jitar.ts b/examples/concepts/node-client/src/jitar.ts deleted file mode 100644 index 6ae887de..00000000 --- a/examples/concepts/node-client/src/jitar.ts +++ /dev/null @@ -1,9 +0,0 @@ - -import { buildServer } from 'jitar'; - -const moduleImporter = async (specifier: string) => import(specifier); -const server = await buildServer(moduleImporter); - -process.on('SIGINT', async () => server.stop()); - -server.start(); diff --git a/examples/concepts/overrides/README.md b/examples/concepts/overrides/README.md deleted file mode 100644 index 16998e5f..00000000 --- a/examples/concepts/overrides/README.md +++ /dev/null @@ -1,44 +0,0 @@ - -# Jitar | Hello World example - -This example demonstrates how to override module imports. - -The application overrides sayHello module with the sayBye module. - -## Project setup - -**Functions** - -* sayBye (`src/sayBye.ts`) -* sayHello (`src/sayHello.ts`) -* saySomething (`src/saySomething.ts`) - -**Segments** - -* Default - contains the *sayHello* and *saySomething* procedure (`segments/default.segment.json`) - -**Services** - -* Standalone (`services/standalone.json`) - -## Running the example - -1\. Install Jitar by running the following command from the root directory of the example. - -```bash -npm install -``` - -2\. Next, build the application by running the following command. - -```bash -npm run build -``` - -3\. Then start Jitar with the following command from the same directory. - -```bash -npm run standalone -``` - -The ``requests.http`` file contains example requests to call the seyHello and saySomething procedures. diff --git a/examples/concepts/overrides/package.json b/examples/concepts/overrides/package.json deleted file mode 100644 index e0df1bda..00000000 --- a/examples/concepts/overrides/package.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "name": "jitar-overrides-example", - "private": true, - "type": "module", - "scripts": { - "build": "tsc", - "standalone": "node --experimental-network-imports dist/jitar.js --config=services/standalone.json" - }, - "dependencies": { - "jitar": "*" - } -} \ No newline at end of file diff --git a/examples/concepts/overrides/requests.http b/examples/concepts/overrides/requests.http deleted file mode 100644 index dbe67ceb..00000000 --- a/examples/concepts/overrides/requests.http +++ /dev/null @@ -1,10 +0,0 @@ - -// Should say bye to John - -GET http://localhost:3000/rpc/saySomething?name=John HTTP/1.1 - -### - -// Should also say bye to John - -GET http://localhost:3000/rpc/sayHello?name=John HTTP/1.1 diff --git a/examples/concepts/overrides/segments/default.segment.json b/examples/concepts/overrides/segments/default.segment.json deleted file mode 100644 index 0f8d78a8..00000000 --- a/examples/concepts/overrides/segments/default.segment.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "./sayHello": { - "default": { - "access": "public", - "version": "0.0.0" - } - }, - "./saySomething": { - "default": { - "access": "public", - "version": "0.0.0" - } - } -} \ No newline at end of file diff --git a/examples/concepts/overrides/services/standalone.json b/examples/concepts/overrides/services/standalone.json deleted file mode 100644 index 461302be..00000000 --- a/examples/concepts/overrides/services/standalone.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "url": "http://127.0.0.1:3000", - "standalone": - { - "overrides": - { - "./sayHello": "./sayBye" - } - } -} \ No newline at end of file diff --git a/examples/concepts/overrides/src/jitar.ts b/examples/concepts/overrides/src/jitar.ts deleted file mode 100644 index 6ae887de..00000000 --- a/examples/concepts/overrides/src/jitar.ts +++ /dev/null @@ -1,9 +0,0 @@ - -import { buildServer } from 'jitar'; - -const moduleImporter = async (specifier: string) => import(specifier); -const server = await buildServer(moduleImporter); - -process.on('SIGINT', async () => server.stop()); - -server.start(); diff --git a/examples/concepts/overrides/src/sayBye.ts b/examples/concepts/overrides/src/sayBye.ts deleted file mode 100644 index 6c371b70..00000000 --- a/examples/concepts/overrides/src/sayBye.ts +++ /dev/null @@ -1,5 +0,0 @@ - -export default async function sayBye(name = 'World'): Promise -{ - return `Bye ${name}`; -} diff --git a/examples/concepts/overrides/src/sayHello.ts b/examples/concepts/overrides/src/sayHello.ts deleted file mode 100644 index f5f5796c..00000000 --- a/examples/concepts/overrides/src/sayHello.ts +++ /dev/null @@ -1,5 +0,0 @@ - -export default async function sayHello(name = 'World'): Promise -{ - return `Hello ${name}`; -} diff --git a/examples/concepts/overrides/src/saySomething.ts b/examples/concepts/overrides/src/saySomething.ts deleted file mode 100644 index b99df1b9..00000000 --- a/examples/concepts/overrides/src/saySomething.ts +++ /dev/null @@ -1,7 +0,0 @@ - -import sayHello from './sayHello'; - -export default async function saySomething(name = 'World'): Promise -{ - return sayHello(name); -} diff --git a/examples/concepts/segmentation/jitar.json b/examples/concepts/segmentation/jitar.json new file mode 100644 index 00000000..4bb49481 --- /dev/null +++ b/examples/concepts/segmentation/jitar.json @@ -0,0 +1,4 @@ +{ + "source": "./dist", + "target": "./.jitar" +} \ No newline at end of file diff --git a/examples/concepts/segmentation/package.json b/examples/concepts/segmentation/package.json index ad204f92..9a21d22e 100644 --- a/examples/concepts/segmentation/package.json +++ b/examples/concepts/segmentation/package.json @@ -3,12 +3,11 @@ "type": "module", "private": true, "scripts": { - "build": "tsc", - "standalone": "node --experimental-network-imports dist/jitar.js --config=services/standalone.json", - "repo": "node --experimental-network-imports dist/jitar.js --config=services/repository.json", - "gateway": "node --experimental-network-imports dist/jitar.js --config=services/gateway.json", - "worker-data": "node --experimental-network-imports dist/jitar.js --config=services/data.json", - "worker-process": "node --experimental-network-imports dist/jitar.js --config=services/process.json" + "build": "rm -rf dist && tsc && jitar build", + "standalone": "jitar start --service=services/standalone.json", + "gateway": "jitar start --service=services/gateway.json", + "worker-data": "jitar start --service=services/data.json", + "worker-process": "jitar start --service=services/process.json" }, "dependencies": { "jitar": "*" diff --git a/examples/concepts/segmentation/services/data.json b/examples/concepts/segmentation/services/data.json index ba463f83..68008d4b 100644 --- a/examples/concepts/segmentation/services/data.json +++ b/examples/concepts/segmentation/services/data.json @@ -3,7 +3,6 @@ "worker": { "gateway": "http://127.0.0.1:3000", - "repository": "http://127.0.0.1:2999", "segments": [ "data" ] } } \ No newline at end of file diff --git a/examples/concepts/segmentation/services/gateway.json b/examples/concepts/segmentation/services/gateway.json index 4c1dfdb1..2a47b940 100644 --- a/examples/concepts/segmentation/services/gateway.json +++ b/examples/concepts/segmentation/services/gateway.json @@ -1,6 +1,4 @@ { "url": "http://127.0.0.1:3000", - "gateway": { - "repository": "http://127.0.0.1:2999" - } + "gateway": { } } \ No newline at end of file diff --git a/examples/concepts/segmentation/services/process.json b/examples/concepts/segmentation/services/process.json index ee772123..b069be88 100644 --- a/examples/concepts/segmentation/services/process.json +++ b/examples/concepts/segmentation/services/process.json @@ -3,7 +3,6 @@ "worker": { "gateway": "http://127.0.0.1:3000", - "repository": "http://127.0.0.1:2999", "segments": [ "process" ] } } \ No newline at end of file diff --git a/examples/concepts/segmentation/services/repository.json b/examples/concepts/segmentation/services/repository.json deleted file mode 100644 index d60c52d8..00000000 --- a/examples/concepts/segmentation/services/repository.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "url": "http://127.0.0.1:2999", - "repository": - { - - } -} \ No newline at end of file diff --git a/examples/concepts/segmentation/services/standalone.json b/examples/concepts/segmentation/services/standalone.json index 9117bcfa..793a83f7 100644 --- a/examples/concepts/segmentation/services/standalone.json +++ b/examples/concepts/segmentation/services/standalone.json @@ -1,7 +1,7 @@ { "url": "http://127.0.0.1:3000", - "standalone": + "worker": { - + "segments": [ "process", "data" ] } } \ No newline at end of file diff --git a/examples/concepts/segmentation/src/jitar.ts b/examples/concepts/segmentation/src/jitar.ts deleted file mode 100644 index 6ae887de..00000000 --- a/examples/concepts/segmentation/src/jitar.ts +++ /dev/null @@ -1,9 +0,0 @@ - -import { buildServer } from 'jitar'; - -const moduleImporter = async (specifier: string) => import(specifier); -const server = await buildServer(moduleImporter); - -process.on('SIGINT', async () => server.stop()); - -server.start(); diff --git a/package-lock.json b/package-lock.json index 1b0cbda4..7acd2c74 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,30 +14,29 @@ "examples/**/*" ], "devDependencies": { - "@rollup/plugin-node-resolve": "^15.2.3", - "@rollup/plugin-replace": "^5.0.7", + "@rollup/plugin-node-resolve": "^15.3.0", + "@rollup/plugin-replace": "^6.0.1", "@rollup/plugin-terser": "^0.4.4", - "@rollup/plugin-typescript": "^11.1.6", + "@rollup/plugin-typescript": "^12.1.0", "@types/express": "^4.17.21", - "@types/express-http-proxy": "^1.6.6", "@types/fs-extra": "^11.0.4", "@types/mime-types": "^2.1.4", "@types/prompts": "^2.4.9", - "@types/yargs": "^17.0.32", "@typescript-eslint/eslint-plugin": "^7.15.0", - "@vitest/coverage-v8": "^1.6.0", - "auto-changelog": "^2.4.0", + "@vitest/coverage-v8": "^2.1.1", + "auto-changelog": "^2.5.0", "degit": "^2.8.4", "eslint": "^8.57.0", "eslint-plugin-jitar": "0.0.1", "eslint-plugin-sonarjs": "^1.0.3", - "lerna": "^8.1.6", - "rollup": "^4.18.0", + "lerna": "^8.1.8", + "rollup": "^4.24.0", "rollup-plugin-dts": "^6.1.1", - "vitest": "^1.6.0" + "vitest": "^2.1.1" } }, "examples/apps/full-stack": { + "name": "jitar-full-stack-example", "version": "0.0.0", "dependencies": { "jitar": "*", @@ -68,72 +67,71 @@ } }, "examples/concepts/access-protection": { + "name": "jitar-access-protection-example", "dependencies": { "jitar": "*" } }, "examples/concepts/construction": { + "name": "jitar-construction-example", "dependencies": { "jitar": "*" } }, "examples/concepts/cors": { + "name": "jitar-cors-example", "dependencies": { "express": "*", "jitar": "*" }, "devDependencies": { - "cpx2": "*", - "npm-run-all": "*", - "rimraf": "*" + "cpx2": "*" } }, "examples/concepts/data-transportation": { + "name": "jitar-data-transportation-example", "dependencies": { "jitar": "*" } }, "examples/concepts/error-handling": { + "name": "jitar-error-handling-example", "dependencies": { "jitar": "*" } }, "examples/concepts/health-checks": { + "name": "jitar-health-checks-example", "dependencies": { "jitar": "*" } }, "examples/concepts/hello-world": { + "name": "jitar-helloworld-example", "dependencies": { "jitar": "*" } }, "examples/concepts/load-balancing": { + "name": "jitar-load-balancing-example", "dependencies": { "jitar": "*" } }, "examples/concepts/middleware": { + "name": "jitar-middleware-example", "dependencies": { "jitar": "*" } }, "examples/concepts/multi-version": { - "dependencies": { - "jitar": "*" - } - }, - "examples/concepts/node-client": { - "dependencies": { - "jitar": "*" - } - }, - "examples/concepts/overrides": { + "name": "jitar-multi-version-example", "dependencies": { "jitar": "*" } }, "examples/concepts/segmentation": { + "name": "jitar-segmentation-example", "dependencies": { "jitar": "*" } @@ -165,9 +163,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.25.2", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.2.tgz", - "integrity": "sha512-bYcppcpKBvX4znYaPEeFau03bp89ShqNMLs+rmdptMw+heSZh9+z84d2YG+K7cYLbWwzdjtDoW/uqZmPjulClQ==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.4.tgz", + "integrity": "sha512-+LGRog6RAsCJrrrg/IO6LGmpphNe5DiK30dGjCoxxeGv49B10/3XYGxPsAwrDlMFcFEvdAUavDT8r9k/hSyQqQ==", "dev": true, "engines": { "node": ">=6.9.0" @@ -213,12 +211,12 @@ } }, "node_modules/@babel/generator": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.0.tgz", - "integrity": "sha512-3LEEcj3PVW8pW2R1SR1M89g/qrYk/m/mB/tLqn7dn4sbBUQyTqnlod+II2U4dqiGtUmkcnAmkMDralTFZttRiw==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.6.tgz", + "integrity": "sha512-VPC82gr1seXOpkjAAKoLhP50vx4vGNlF4msF64dSFq1P8RfB+QAuJWGHPXXPc8QyfVWwwB/TNNU4+ayZmHNbZw==", "dev": true, "dependencies": { - "@babel/types": "^7.25.0", + "@babel/types": "^7.25.6", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^2.5.1" @@ -243,15 +241,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "dependencies": { - "yallist": "^3.0.2" - } - }, "node_modules/@babel/helper-compilation-targets/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -261,12 +250,6 @@ "semver": "bin/semver.js" } }, - "node_modules/@babel/helper-compilation-targets/node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, "node_modules/@babel/helper-module-imports": { "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz", @@ -348,13 +331,13 @@ } }, "node_modules/@babel/helpers": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.0.tgz", - "integrity": "sha512-MjgLZ42aCm0oGjJj8CtSM3DB8NOOf8h2l7DCTePJs29u+v7yO/RBX9nShlKMgFnRks/Q4tBAe7Hxnov9VkGwLw==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.6.tgz", + "integrity": "sha512-Xg0tn4HcfTijTwfDwYlvVCl43V6h4KyVVX2aEm4qdO/PC6L2YvzLHFdmxhoeSA3eslcE6+ZVXHgWwopXYLNq4Q==", "dev": true, "dependencies": { "@babel/template": "^7.25.0", - "@babel/types": "^7.25.0" + "@babel/types": "^7.25.6" }, "engines": { "node": ">=6.9.0" @@ -375,84 +358,13 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/highlight/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/@babel/parser": { - "version": "7.25.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.3.tgz", - "integrity": "sha512-iLTJKDbJ4hMvFPgQwwsVoxtHyWpKKPBrxkANrSYewDPaPpT5py5yeVkgPIJ7XYXhndxJpaA3PyALSXQ7u8e/Dw==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.6.tgz", + "integrity": "sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q==", "dev": true, "dependencies": { - "@babel/types": "^7.25.2" + "@babel/types": "^7.25.6" }, "bin": { "parser": "bin/babel-parser.js" @@ -506,16 +418,16 @@ } }, "node_modules/@babel/traverse": { - "version": "7.25.3", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.3.tgz", - "integrity": "sha512-HefgyP1x754oGCsKmV5reSmtV7IXj/kpaE1XYY+D9G5PvKKoFfSbiS4M77MdjuwlZKDIKFCffq9rPU+H/s3ZdQ==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.6.tgz", + "integrity": "sha512-9Vrcx5ZW6UwK5tvqsj0nGpp/XzqthkT0dqIc9g1AdtygFToNtTF67XzYS//dm+SAK9cp3B9R4ZO/46p63SCjlQ==", "dev": true, "dependencies": { "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.25.0", - "@babel/parser": "^7.25.3", + "@babel/generator": "^7.25.6", + "@babel/parser": "^7.25.6", "@babel/template": "^7.25.0", - "@babel/types": "^7.25.2", + "@babel/types": "^7.25.6", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -523,19 +435,10 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/traverse/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/@babel/types": { - "version": "7.25.2", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.2.tgz", - "integrity": "sha512-YTnYtra7W9e6/oAZEHj0bJehPRUlLH9/fbpT5LfB0NhQXyALCRkRs3zH9v07IYhkgpqX6Z78FnuccZr/l4Fs4Q==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.6.tgz", + "integrity": "sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw==", "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.24.8", @@ -964,9 +867,9 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz", - "integrity": "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==", + "version": "4.11.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.1.tgz", + "integrity": "sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==", "dev": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" @@ -1005,6 +908,21 @@ "concat-map": "0.0.1" } }, + "node_modules/@eslint/eslintrc/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/@eslint/eslintrc/node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -1017,23 +935,35 @@ "node": "*" } }, + "node_modules/@eslint/eslintrc/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "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==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "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==", + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", "deprecated": "Use @eslint/config-array instead", "dev": true, "dependencies": { - "@humanwhocodes/object-schema": "^2.0.2", + "@humanwhocodes/object-schema": "^2.0.3", "debug": "^4.3.1", "minimatch": "^3.0.5" }, @@ -1109,9 +1039,9 @@ } }, "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==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "engines": { "node": ">=12" }, @@ -1208,16 +1138,48 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@jitar/caching": { - "resolved": "packages/caching", + "node_modules/@jitar/analysis": { + "resolved": "packages/analysis", "link": true }, - "node_modules/@jitar/plugin-vite": { - "resolved": "packages/plugin-vite", + "node_modules/@jitar/build": { + "resolved": "packages/build", + "link": true + }, + "node_modules/@jitar/cli": { + "resolved": "packages/cli", + "link": true + }, + "node_modules/@jitar/configuration": { + "resolved": "packages/configuration", + "link": true + }, + "node_modules/@jitar/errors": { + "resolved": "packages/errors", + "link": true + }, + "node_modules/@jitar/execution": { + "resolved": "packages/execution", + "link": true + }, + "node_modules/@jitar/health": { + "resolved": "packages/health", + "link": true + }, + "node_modules/@jitar/http": { + "resolved": "packages/http", + "link": true + }, + "node_modules/@jitar/logging": { + "resolved": "packages/logging", + "link": true + }, + "node_modules/@jitar/middleware": { + "resolved": "packages/middleware", "link": true }, - "node_modules/@jitar/reflection": { - "resolved": "packages/reflection", + "node_modules/@jitar/plugin-vite": { + "resolved": "packages/plugin-vite", "link": true }, "node_modules/@jitar/runtime": { @@ -1228,8 +1190,16 @@ "resolved": "packages/serialization", "link": true }, - "node_modules/@jitar/server-nodejs": { - "resolved": "packages/server-nodejs", + "node_modules/@jitar/services": { + "resolved": "packages/services", + "link": true + }, + "node_modules/@jitar/sourcing": { + "resolved": "packages/sourcing", + "link": true + }, + "node_modules/@jitar/validation": { + "resolved": "packages/validation", "link": true }, "node_modules/@jridgewell/gen-mapping": { @@ -1291,12 +1261,12 @@ } }, "node_modules/@lerna/create": { - "version": "8.1.7", - "resolved": "https://registry.npmjs.org/@lerna/create/-/create-8.1.7.tgz", - "integrity": "sha512-ch81CgU5pBNOiUCQx44F/ZtN4DxxJjUQtuytYRBFWJSHAJ+XPJtiC/yQ9zjr1I1yaUlmNYYblkopoOyziOdJ1w==", + "version": "8.1.8", + "resolved": "https://registry.npmjs.org/@lerna/create/-/create-8.1.8.tgz", + "integrity": "sha512-wi72R01tgjBjzG2kjRyTHl4yCTKDfDMIXRyKz9E/FBa9SkFvUOAE4bdyY9MhEsRZmSWL7+CYE8Flv/HScRpBbA==", "dev": true, "dependencies": { - "@npmcli/arborist": "7.5.3", + "@npmcli/arborist": "7.5.4", "@npmcli/package-json": "5.2.0", "@npmcli/run-script": "8.1.0", "@nx/devkit": ">=17.1.2 < 20", @@ -1372,6 +1342,21 @@ "node": ">=18.0.0" } }, + "node_modules/@lerna/create/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/@lerna/create/node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -1398,6 +1383,24 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/@lerna/create/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@lerna/create/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, "node_modules/@lerna/create/node_modules/glob": { "version": "9.3.5", "resolved": "https://registry.npmjs.org/glob/-/glob-9.3.5.tgz", @@ -1440,6 +1443,15 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/@lerna/create/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/@lerna/create/node_modules/minimatch": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.5.tgz", @@ -1508,10 +1520,44 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/@lerna/create/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@lerna/create/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true + }, + "node_modules/@lerna/create/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==", + "dev": true + }, + "node_modules/@lerna/create/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==", + "dev": true, + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/@mongodb-js/saslprep": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.8.tgz", - "integrity": "sha512-qKwC/M/nNNaKUBMQ0nuzm47b7ZYWQHN3pcXq4IIcoSBc2hOIrflAxJduIvvqmhoz3gR2TacTAs8vlsCVPkiEdQ==", + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.9.tgz", + "integrity": "sha512-tVkljjeEaAhCqTzajSdgbQ6gE6f3oneVwa3iXR6csiEwXXOFsiC6Uh9iAjAhXPtqa/XMDHWjjeNH/77m/Yq2dw==", "dependencies": { "sparse-bitfield": "^3.0.3" } @@ -1578,10 +1624,16 @@ "node": "^16.14.0 || >=18.0.0" } }, + "node_modules/@npmcli/agent/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 + }, "node_modules/@npmcli/arborist": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@npmcli/arborist/-/arborist-7.5.3.tgz", - "integrity": "sha512-7gbMdDNSYUzi0j2mpb6FoXRg3BxXWplMQZH1MZlvNjSdWFObaUz2Ssvo0Nlh2xmWks1OPo+gpsE6qxpT/5M7lQ==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/@npmcli/arborist/-/arborist-7.5.4.tgz", + "integrity": "sha512-nWtIc6QwwoUORCRNzKx4ypHqCk3drI+5aeYdMTQQiRCcn4lOOgfQh7WyZobGYTxXPSq1VwV53lkpN/BRlRk08g==", "dev": true, "dependencies": { "@isaacs/string-locale-compare": "^1.1.0", @@ -1627,6 +1679,12 @@ "node": "^16.14.0 || >=18.0.0" } }, + "node_modules/@npmcli/arborist/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 + }, "node_modules/@npmcli/fs": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.1.tgz", @@ -1677,6 +1735,12 @@ "node": ">=16" } }, + "node_modules/@npmcli/git/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 + }, "node_modules/@npmcli/git/node_modules/which": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", @@ -1874,21 +1938,21 @@ } }, "node_modules/@nrwl/devkit": { - "version": "19.5.4", - "resolved": "https://registry.npmjs.org/@nrwl/devkit/-/devkit-19.5.4.tgz", - "integrity": "sha512-T3cRQErKfEyrx9x+xsnY4kg5+vmwPn3UQY1GwsPuuhqYeJn2NAQFzb8gcnZ6mSTqughum3eqp2nNDmpUkWO7tg==", + "version": "19.8.3", + "resolved": "https://registry.npmjs.org/@nrwl/devkit/-/devkit-19.8.3.tgz", + "integrity": "sha512-67vZJRMCEA543A0uz8dPTZ5lX4wsAlgsr24KJafsUxBC2WCf9z4BqcLj0jVWfmRdKJmu2UwaxtD2UB1bekt3sg==", "dev": true, "dependencies": { - "@nx/devkit": "19.5.4" + "@nx/devkit": "19.8.3" } }, "node_modules/@nrwl/tao": { - "version": "19.5.4", - "resolved": "https://registry.npmjs.org/@nrwl/tao/-/tao-19.5.4.tgz", - "integrity": "sha512-LNCi+2Rb17wNkUUdX2OQPRv9qOrstlmuAAA9VVcIcW78NdybjgWWvMIhf4NrAkjn7/uALrZdv22zyiGekmheDw==", + "version": "19.8.3", + "resolved": "https://registry.npmjs.org/@nrwl/tao/-/tao-19.8.3.tgz", + "integrity": "sha512-byjBtOXx+xGjMu1wKopJSJbrR3gKqTsCEgp1+YSZ45+iFKxFdXLJrGsyhVqBovCKVBM+5/KtGuEkZoUPlP8JWg==", "dev": true, "dependencies": { - "nx": "19.5.4", + "nx": "19.8.3", "tslib": "^2.3.0" }, "bin": { @@ -1896,12 +1960,12 @@ } }, "node_modules/@nx/devkit": { - "version": "19.5.4", - "resolved": "https://registry.npmjs.org/@nx/devkit/-/devkit-19.5.4.tgz", - "integrity": "sha512-0TG2iU0xVRuElLP2aLeRSKUynsC+UgHqE/FJW2IcglHngs2/Duw2A4HDUVVOxztkEQPmp736qkYSwFO0nlOGxg==", + "version": "19.8.3", + "resolved": "https://registry.npmjs.org/@nx/devkit/-/devkit-19.8.3.tgz", + "integrity": "sha512-uX50CAM11tzhwswf0ftN0QfzW2FM3M4Mf/pD/nRRnmsTkcPTdMXVu4LHuLVTp4CMsaO+cOQlqgHXujHYfOIctg==", "dev": true, "dependencies": { - "@nrwl/devkit": "19.5.4", + "@nrwl/devkit": "19.8.3", "ejs": "^3.1.7", "enquirer": "~2.3.6", "ignore": "^5.0.4", @@ -1931,9 +1995,9 @@ } }, "node_modules/@nx/nx-darwin-arm64": { - "version": "19.5.4", - "resolved": "https://registry.npmjs.org/@nx/nx-darwin-arm64/-/nx-darwin-arm64-19.5.4.tgz", - "integrity": "sha512-s+OmSsYUtECmEKAdzSsYoO9vamx+njiP72eSZusmTh7fCJg+dW3dcifRkUf3h1dcM53hffXcmxKEoWxZMAeuXw==", + "version": "19.8.3", + "resolved": "https://registry.npmjs.org/@nx/nx-darwin-arm64/-/nx-darwin-arm64-19.8.3.tgz", + "integrity": "sha512-ORHFFWMZcvFi0xcpCaXccXVEhFwAevSHOIKfW359+12H9w7VW2O42B+2NcVMK1mrDTOjlXTd+0AmAu7P4NzWFA==", "cpu": [ "arm64" ], @@ -1947,9 +2011,9 @@ } }, "node_modules/@nx/nx-darwin-x64": { - "version": "19.5.4", - "resolved": "https://registry.npmjs.org/@nx/nx-darwin-x64/-/nx-darwin-x64-19.5.4.tgz", - "integrity": "sha512-GjA6aThF9P7FR3OdNZn4g9c1bJeQMOdQmo2jaBaGmUPnOIZSEWinHkvh5g8vDg+jNwRdHKK84jJWWW0/o73iYQ==", + "version": "19.8.3", + "resolved": "https://registry.npmjs.org/@nx/nx-darwin-x64/-/nx-darwin-x64-19.8.3.tgz", + "integrity": "sha512-Ji9DPA0tuzygMcypD/FHRDQSPipcRqMNmSaNKxVpcCbozVTWHvqXFk0rloDIUnxnE0+zvE9LN71H2sS4ZHdTQA==", "cpu": [ "x64" ], @@ -1963,9 +2027,9 @@ } }, "node_modules/@nx/nx-freebsd-x64": { - "version": "19.5.4", - "resolved": "https://registry.npmjs.org/@nx/nx-freebsd-x64/-/nx-freebsd-x64-19.5.4.tgz", - "integrity": "sha512-KPVTmg2NpvON3+sh2pNWv2GJow5CL3fX2xBo4cI9D50DDZOD4fB68S2v5q6nLC1QWOwQcC0PLnSpoKaDB0PgQg==", + "version": "19.8.3", + "resolved": "https://registry.npmjs.org/@nx/nx-freebsd-x64/-/nx-freebsd-x64-19.8.3.tgz", + "integrity": "sha512-Ys+PqtBZCS+QBNs7he3fnxVhMWz/lSSaBVUlVHoQcV1Y4clEpP2TWNQSsbaVnnpcB7pdmKN5ymWdaCaAQuqCMw==", "cpu": [ "x64" ], @@ -1979,9 +2043,9 @@ } }, "node_modules/@nx/nx-linux-arm-gnueabihf": { - "version": "19.5.4", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-19.5.4.tgz", - "integrity": "sha512-a535HwxVhTS+ngcoFxrsqmggpsKWquubILZhIeY/q+XW6nX61FEb/EqlMkc+aJLHD1LQBGax1W+j7YvTA/66Lw==", + "version": "19.8.3", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-19.8.3.tgz", + "integrity": "sha512-hGOlML60ELXkgkqLHB/w/sXbTbXFhOQGSXC72CjaP5G0u1gj8eTQKJ7WEsqPAFMk5SLFFxqM7eid0LmAYYuZWQ==", "cpu": [ "arm" ], @@ -1995,9 +2059,9 @@ } }, "node_modules/@nx/nx-linux-arm64-gnu": { - "version": "19.5.4", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-19.5.4.tgz", - "integrity": "sha512-eRu/IoPB68MQeEmfyub+P79eDYvXOyNa706rp0JnDHL5LMw12kPF3MIeqc/v7o6xWakGHCSnTCulcqsl8HXryg==", + "version": "19.8.3", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-19.8.3.tgz", + "integrity": "sha512-K/5iVbLbhsx28YtZHvveJgF41rbr2kMdabooZeFqy6VReN7U/zGJMjpV1FzDlf3TNr9jyjPDZgVQRS+qXau2qA==", "cpu": [ "arm64" ], @@ -2011,9 +2075,9 @@ } }, "node_modules/@nx/nx-linux-arm64-musl": { - "version": "19.5.4", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-musl/-/nx-linux-arm64-musl-19.5.4.tgz", - "integrity": "sha512-r5NNVngNwTe+zpUAAZAgCezDkjc0pi2zrr8VwiaRZsmVjhHtvvsXJgo1ONw5s2HjKoKuTFEa5jKTUlAHkaQ7Kg==", + "version": "19.8.3", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-musl/-/nx-linux-arm64-musl-19.8.3.tgz", + "integrity": "sha512-zqzWjFniZDXiI/3MYxbJ0yIenUKr56apLy70oABTBHx++dsUA3/DxLMNypMA82a8KQtsbePWUi3Pgtr+JIMNXw==", "cpu": [ "arm64" ], @@ -2027,9 +2091,9 @@ } }, "node_modules/@nx/nx-linux-x64-gnu": { - "version": "19.5.4", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-gnu/-/nx-linux-x64-gnu-19.5.4.tgz", - "integrity": "sha512-8TWwjyp/bK2a/CHK2HuC7I8iITC9ytEvfru8/kw1mSyoK4kSDlzkL/1uDl536ULXLWORulfEzaGb61GynVc1vg==", + "version": "19.8.3", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-gnu/-/nx-linux-x64-gnu-19.8.3.tgz", + "integrity": "sha512-W1RRCqsQvpur4BxP5g5cQwjZB6jhxYLSSXi3QQDaU5ITkaV5Pdj/L7D/G6YgRB8lzKZrXc57aLJ5UKY/Z+di7w==", "cpu": [ "x64" ], @@ -2043,9 +2107,9 @@ } }, "node_modules/@nx/nx-linux-x64-musl": { - "version": "19.5.4", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-musl/-/nx-linux-x64-musl-19.5.4.tgz", - "integrity": "sha512-5Pf32iv9nnmSV/oOHd9k/5L45m3BooSj096G/ejAN3BHMr4CZIMhjDcQq9ZX7pAZFchU5zL0+dNClK70QfA7PA==", + "version": "19.8.3", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-musl/-/nx-linux-x64-musl-19.8.3.tgz", + "integrity": "sha512-waTo0zBBGnmU7fS87IpOnVGx7EHa0umzSMlGG0LUoU6swOeNODezsBn1Vbvaw1o7sStWBzdEBlxLxHOQXRAidg==", "cpu": [ "x64" ], @@ -2059,9 +2123,9 @@ } }, "node_modules/@nx/nx-win32-arm64-msvc": { - "version": "19.5.4", - "resolved": "https://registry.npmjs.org/@nx/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-19.5.4.tgz", - "integrity": "sha512-fyKGfde4Pq9r5qQMLIleujq7B5ta86y8RSPUruoN6zaGrNg6waqbpMdZUjjsg9L7PP9RPaMHPMubC21OnQQomQ==", + "version": "19.8.3", + "resolved": "https://registry.npmjs.org/@nx/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-19.8.3.tgz", + "integrity": "sha512-lio7ulblEMs1otMtVIrdfdMTBqKRZEHim57AcMHSVnwmtl2ENP6TR3YIgyigjfLlkPanNU7i0QQ4h6Nk2I/FRw==", "cpu": [ "arm64" ], @@ -2075,9 +2139,9 @@ } }, "node_modules/@nx/nx-win32-x64-msvc": { - "version": "19.5.4", - "resolved": "https://registry.npmjs.org/@nx/nx-win32-x64-msvc/-/nx-win32-x64-msvc-19.5.4.tgz", - "integrity": "sha512-gcAr5zZQKiAxHZ7iUOVeMLf/KIh4EFbF07Q0uSmgGmUJL1u3mZTjeG57V1AMZbTQESGY43rgoymqVYkghc5Jlw==", + "version": "19.8.3", + "resolved": "https://registry.npmjs.org/@nx/nx-win32-x64-msvc/-/nx-win32-x64-msvc-19.8.3.tgz", + "integrity": "sha512-RU11iXJzdrw5CmogT2AwsjxK7g8vWf6Oy23NlrvsQFODtavjqAWoD5qpUY/H16s9lVDwrpzCbGbAXph0lbgLKA==", "cpu": [ "x64" ], @@ -2271,21 +2335,21 @@ "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, "optional": true, "engines": { "node": ">=14" } }, "node_modules/@rollup/plugin-node-resolve": { - "version": "15.2.3", - "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.3.tgz", - "integrity": "sha512-j/lym8nf5E21LwBT4Df1VD6hRO2L2iwUeUmP7litikRsVp1H6NWx20NEp0Y7su+7XGc476GnXXc4kFeZNGmaSQ==", + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.3.0.tgz", + "integrity": "sha512-9eO5McEICxMzJpDW9OnMYSv4Sta3hmt7VtBFz5zR9273suNOydOyq/FrGeGy+KsTRFm8w0SLVhzig2ILFT63Ag==", "dev": true, "dependencies": { "@rollup/pluginutils": "^5.0.1", "@types/resolve": "1.20.2", "deepmerge": "^4.2.2", - "is-builtin-module": "^3.2.1", "is-module": "^1.0.0", "resolve": "^1.22.1" }, @@ -2302,9 +2366,9 @@ } }, "node_modules/@rollup/plugin-replace": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-5.0.7.tgz", - "integrity": "sha512-PqxSfuorkHz/SPpyngLyg5GCEkOcee9M1bkxiVDr41Pd61mqP1PLOoDPbpl44SB2mQGKwV/In74gqQmGITOhEQ==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-6.0.1.tgz", + "integrity": "sha512-2sPh9b73dj5IxuMmDAsQWVFT7mR+yoHweBaXG2W/R8vQ+IWZlnaI7BR7J6EguVQUp1hd8Z7XuozpDjEKQAAC2Q==", "dev": true, "dependencies": { "@rollup/pluginutils": "^5.0.1", @@ -2345,9 +2409,9 @@ } }, "node_modules/@rollup/plugin-typescript": { - "version": "11.1.6", - "resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-11.1.6.tgz", - "integrity": "sha512-R92yOmIACgYdJ7dJ97p4K69I8gg6IEHt8M7dUBxN3W6nrO8uUxX5ixl0yU/N3aZTi8WhPuICvOHXQvF6FaykAA==", + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-12.1.0.tgz", + "integrity": "sha512-Kzs8KGJofe7cfTRODsnG1jNGxSvU8gVoNNd7Z/QaY25AYwe2LSSUpx/kPxqF38NYkpR8de3m51r9uwJpDlz6dg==", "dev": true, "dependencies": { "@rollup/pluginutils": "^5.1.0", @@ -2371,9 +2435,9 @@ } }, "node_modules/@rollup/pluginutils": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.0.tgz", - "integrity": "sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.2.tgz", + "integrity": "sha512-/FIdS3PyZ39bjZlwqFnWqCOVnW7o963LtKMwQOD0NhQqw22gSr2YY1afu3FxRip4ZCZNsD5jq6Aaz6QV3D/Njw==", "dev": true, "dependencies": { "@types/estree": "^1.0.0", @@ -2393,9 +2457,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.19.1.tgz", - "integrity": "sha512-XzqSg714++M+FXhHfXpS1tDnNZNpgxxuGZWlRG/jSj+VEPmZ0yg6jV4E0AL3uyBKxO8mO3xtOsP5mQ+XLfrlww==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.0.tgz", + "integrity": "sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA==", "cpu": [ "arm" ], @@ -2406,9 +2470,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.19.1.tgz", - "integrity": "sha512-thFUbkHteM20BGShD6P08aungq4irbIZKUNbG70LN8RkO7YztcGPiKTTGZS7Kw+x5h8hOXs0i4OaHwFxlpQN6A==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.0.tgz", + "integrity": "sha512-ijLnS1qFId8xhKjT81uBHuuJp2lU4x2yxa4ctFPtG+MqEE6+C5f/+X/bStmxapgmwLwiL3ih122xv8kVARNAZA==", "cpu": [ "arm64" ], @@ -2419,9 +2483,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.19.1.tgz", - "integrity": "sha512-8o6eqeFZzVLia2hKPUZk4jdE3zW7LCcZr+MD18tXkgBBid3lssGVAYuox8x6YHoEPDdDa9ixTaStcmx88lio5Q==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.0.tgz", + "integrity": "sha512-bIv+X9xeSs1XCk6DVvkO+S/z8/2AMt/2lMqdQbMrmVpgFvXlmde9mLcbQpztXm1tajC3raFDqegsH18HQPMYtA==", "cpu": [ "arm64" ], @@ -2432,9 +2496,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.19.1.tgz", - "integrity": "sha512-4T42heKsnbjkn7ovYiAdDVRRWZLU9Kmhdt6HafZxFcUdpjlBlxj4wDrt1yFWLk7G4+E+8p2C9tcmSu0KA6auGA==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.0.tgz", + "integrity": "sha512-X6/nOwoFN7RT2svEQWUsW/5C/fYMBe4fnLK9DQk4SX4mgVBiTA9h64kjUYPvGQ0F/9xwJ5U5UfTbl6BEjaQdBQ==", "cpu": [ "x64" ], @@ -2445,9 +2509,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.19.1.tgz", - "integrity": "sha512-MXg1xp+e5GhZ3Vit1gGEyoC+dyQUBy2JgVQ+3hUrD9wZMkUw/ywgkpK7oZgnB6kPpGrxJ41clkPPnsknuD6M2Q==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.0.tgz", + "integrity": "sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA==", "cpu": [ "arm" ], @@ -2458,9 +2522,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.19.1.tgz", - "integrity": "sha512-DZNLwIY4ftPSRVkJEaxYkq7u2zel7aah57HESuNkUnz+3bZHxwkCUkrfS2IWC1sxK6F2QNIR0Qr/YXw7nkF3Pw==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.0.tgz", + "integrity": "sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw==", "cpu": [ "arm" ], @@ -2471,9 +2535,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.19.1.tgz", - "integrity": "sha512-C7evongnjyxdngSDRRSQv5GvyfISizgtk9RM+z2biV5kY6S/NF/wta7K+DanmktC5DkuaJQgoKGf7KUDmA7RUw==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.0.tgz", + "integrity": "sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA==", "cpu": [ "arm64" ], @@ -2484,9 +2548,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.19.1.tgz", - "integrity": "sha512-89tFWqxfxLLHkAthAcrTs9etAoBFRduNfWdl2xUs/yLV+7XDrJ5yuXMHptNqf1Zw0UCA3cAutkAiAokYCkaPtw==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.0.tgz", + "integrity": "sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw==", "cpu": [ "arm64" ], @@ -2497,9 +2561,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.19.1.tgz", - "integrity": "sha512-PromGeV50sq+YfaisG8W3fd+Cl6mnOOiNv2qKKqKCpiiEke2KiKVyDqG/Mb9GWKbYMHj5a01fq/qlUR28PFhCQ==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.0.tgz", + "integrity": "sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw==", "cpu": [ "ppc64" ], @@ -2510,9 +2574,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.19.1.tgz", - "integrity": "sha512-/1BmHYh+iz0cNCP0oHCuF8CSiNj0JOGf0jRlSo3L/FAyZyG2rGBuKpkZVH9YF+x58r1jgWxvm1aRg3DHrLDt6A==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.0.tgz", + "integrity": "sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg==", "cpu": [ "riscv64" ], @@ -2523,9 +2587,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.19.1.tgz", - "integrity": "sha512-0cYP5rGkQWRZKy9/HtsWVStLXzCF3cCBTRI+qRL8Z+wkYlqN7zrSYm6FuY5Kd5ysS5aH0q5lVgb/WbG4jqXN1Q==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.0.tgz", + "integrity": "sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g==", "cpu": [ "s390x" ], @@ -2536,9 +2600,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.19.1.tgz", - "integrity": "sha512-XUXeI9eM8rMP8aGvii/aOOiMvTs7xlCosq9xCjcqI9+5hBxtjDpD+7Abm1ZhVIFE1J2h2VIg0t2DX/gjespC2Q==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.0.tgz", + "integrity": "sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A==", "cpu": [ "x64" ], @@ -2549,9 +2613,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.19.1.tgz", - "integrity": "sha512-V7cBw/cKXMfEVhpSvVZhC+iGifD6U1zJ4tbibjjN+Xi3blSXaj/rJynAkCFFQfoG6VZrAiP7uGVzL440Q6Me2Q==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.0.tgz", + "integrity": "sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ==", "cpu": [ "x64" ], @@ -2562,9 +2626,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.19.1.tgz", - "integrity": "sha512-88brja2vldW/76jWATlBqHEoGjJLRnP0WOEKAUbMcXaAZnemNhlAHSyj4jIwMoP2T750LE9lblvD4e2jXleZsA==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.0.tgz", + "integrity": "sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ==", "cpu": [ "arm64" ], @@ -2575,9 +2639,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.19.1.tgz", - "integrity": "sha512-LdxxcqRVSXi6k6JUrTah1rHuaupoeuiv38du8Mt4r4IPer3kwlTo+RuvfE8KzZ/tL6BhaPlzJ3835i6CxrFIRQ==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.0.tgz", + "integrity": "sha512-xrNcGDU0OxVcPTH/8n/ShH4UevZxKIO6HJFK0e15XItZP2UcaiLFd5kiX7hJnqCbSztUF8Qot+JWBC/QXRPYWQ==", "cpu": [ "ia32" ], @@ -2588,9 +2652,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.19.1.tgz", - "integrity": "sha512-2bIrL28PcK3YCqD9anGxDxamxdiJAxA+l7fWIwM5o8UqNy1t3d1NdAweO2XhA0KTDJ5aH1FsuiT5+7VhtHliXg==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.0.tgz", + "integrity": "sha512-fbMkAF7fufku0N2dE5TBXcNlg0pt0cJue4xBRE2Qc5Vqikxr4VCgKj/ht6SMdFcOacVA9rqF70APJ8RN/4vMJw==", "cpu": [ "x64" ], @@ -2772,9 +2836,9 @@ } }, "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==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", "devOptional": true }, "node_modules/@types/express": { @@ -2789,19 +2853,10 @@ "@types/serve-static": "*" } }, - "node_modules/@types/express-http-proxy": { - "version": "1.6.6", - "resolved": "https://registry.npmjs.org/@types/express-http-proxy/-/express-http-proxy-1.6.6.tgz", - "integrity": "sha512-J8ZqHG76rq1UB716IZ3RCmUhg406pbWxsM3oFCFccl5xlWUPzoR4if6Og/cE4juK8emH0H9quZa5ltn6ZdmQJg==", - "dev": true, - "dependencies": { - "@types/express": "*" - } - }, "node_modules/@types/express-serve-static-core": { - "version": "4.19.5", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.5.tgz", - "integrity": "sha512-y6W03tvrACO72aijJ5uF02FRq5cgDR9lUxddQ8vyF+GvmjJQqbzDcJngEjURc+ZsG31VI3hODNZJ2URj86pzmg==", + "version": "4.19.6", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", + "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", "dev": true, "dependencies": { "@types/node": "*", @@ -2860,12 +2915,12 @@ "dev": true }, "node_modules/@types/node": { - "version": "22.0.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.0.2.tgz", - "integrity": "sha512-yPL6DyFwY5PiMVEwymNeqUTKsDczQBJ/5T7W/46RwLU/VH+AA8aT5TZkvBviLKLbbm0hlfftEkGrNzfRk/fofQ==", + "version": "22.7.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.4.tgz", + "integrity": "sha512-y+NPi1rFzDs1NdQHHToqeiX2TIS79SWEAw9GYhkkx8bD0ChpfqC+n2j5OXOCpzfojBEBt6DnEnnG9MY0zk1XLg==", "dev": true, "dependencies": { - "undici-types": "~6.11.1" + "undici-types": "~6.19.2" } }, "node_modules/@types/normalize-package-data": { @@ -2885,15 +2940,15 @@ } }, "node_modules/@types/prop-types": { - "version": "15.7.12", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", - "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==", + "version": "15.7.13", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz", + "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==", "dev": true }, "node_modules/@types/qs": { - "version": "6.9.15", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.15.tgz", - "integrity": "sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==", + "version": "6.9.16", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.16.tgz", + "integrity": "sha512-7i+zxXdPD0T4cKDuxCUXJ4wHcsJLwENa6Z3dCu8cfCK743OGy5Nu1RmAGqDPsoTDINVEcdXKRvR/zre+P2Ku1A==", "dev": true }, "node_modules/@types/range-parser": { @@ -2903,9 +2958,9 @@ "dev": true }, "node_modules/@types/react": { - "version": "18.3.3", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.3.tgz", - "integrity": "sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==", + "version": "18.3.10", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.10.tgz", + "integrity": "sha512-02sAAlBnP39JgXwkAq3PeU9DVaaGpZyF3MGcC0MKgQVkZor5IiiDAipVaxQHtDJAmO4GIy/rVBy/LzVj76Cyqg==", "dev": true, "dependencies": { "@types/prop-types": "*", @@ -2961,21 +3016,6 @@ "@types/webidl-conversions": "*" } }, - "node_modules/@types/yargs": { - "version": "17.0.32", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", - "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", - "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", - "dev": true - }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "7.18.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.18.0.tgz", @@ -3169,14 +3209,14 @@ "dev": true }, "node_modules/@vitejs/plugin-react": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.1.tgz", - "integrity": "sha512-m/V2syj5CuVnaxcUJOQRel/Wr31FFXRFlnOoq1TVtkCxsY5veGMTEmpWHndrhB2U8ScHtCQB1e+4hWYExQc6Lg==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.2.tgz", + "integrity": "sha512-hieu+o05v4glEBucTcKMK3dlES0OeJlD9YVOAPraVMOInBCwzumaIFiUjr4bHK7NPgnAHgiskUoceKercrN8vg==", "dev": true, "dependencies": { - "@babel/core": "^7.24.5", - "@babel/plugin-transform-react-jsx-self": "^7.24.5", - "@babel/plugin-transform-react-jsx-source": "^7.24.1", + "@babel/core": "^7.25.2", + "@babel/plugin-transform-react-jsx-self": "^7.24.7", + "@babel/plugin-transform-react-jsx-source": "^7.24.7", "@types/babel__core": "^7.20.5", "react-refresh": "^0.14.2" }, @@ -3188,137 +3228,153 @@ } }, "node_modules/@vitest/coverage-v8": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-1.6.0.tgz", - "integrity": "sha512-KvapcbMY/8GYIG0rlwwOKCVNRc0OL20rrhFkg/CHNzncV03TE2XWvO5w9uZYoxNiMEBacAJt3unSOiZ7svePew==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-2.1.1.tgz", + "integrity": "sha512-md/A7A3c42oTT8JUHSqjP5uKTWJejzUW4jalpvs+rZ27gsURsMU8DEb+8Jf8C6Kj2gwfSHJqobDNBuoqlm0cFw==", "dev": true, "dependencies": { - "@ampproject/remapping": "^2.2.1", + "@ampproject/remapping": "^2.3.0", "@bcoe/v8-coverage": "^0.2.3", - "debug": "^4.3.4", + "debug": "^4.3.6", "istanbul-lib-coverage": "^3.2.2", "istanbul-lib-report": "^3.0.1", - "istanbul-lib-source-maps": "^5.0.4", - "istanbul-reports": "^3.1.6", - "magic-string": "^0.30.5", - "magicast": "^0.3.3", - "picocolors": "^1.0.0", - "std-env": "^3.5.0", - "strip-literal": "^2.0.0", - "test-exclude": "^6.0.0" + "istanbul-lib-source-maps": "^5.0.6", + "istanbul-reports": "^3.1.7", + "magic-string": "^0.30.11", + "magicast": "^0.3.4", + "std-env": "^3.7.0", + "test-exclude": "^7.0.1", + "tinyrainbow": "^1.2.0" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "vitest": "1.6.0" + "@vitest/browser": "2.1.1", + "vitest": "2.1.1" + }, + "peerDependenciesMeta": { + "@vitest/browser": { + "optional": true + } } }, "node_modules/@vitest/expect": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.6.0.tgz", - "integrity": "sha512-ixEvFVQjycy/oNgHjqsL6AZCDduC+tflRluaHIzKIsdbzkLn2U/iBnVeJwB6HsIjQBdfMR8Z0tRxKUsvFJEeWQ==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.1.tgz", + "integrity": "sha512-YeueunS0HiHiQxk+KEOnq/QMzlUuOzbU1Go+PgAsHvvv3tUkJPm9xWt+6ITNTlzsMXUjmgm5T+U7KBPK2qQV6w==", "dev": true, "dependencies": { - "@vitest/spy": "1.6.0", - "@vitest/utils": "1.6.0", - "chai": "^4.3.10" + "@vitest/spy": "2.1.1", + "@vitest/utils": "2.1.1", + "chai": "^5.1.1", + "tinyrainbow": "^1.2.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, - "node_modules/@vitest/runner": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.6.0.tgz", - "integrity": "sha512-P4xgwPjwesuBiHisAVz/LSSZtDjOTPYZVmNAnpHHSR6ONrf8eCJOFRvUwdHn30F5M1fxhqtl7QZQUk2dprIXAg==", + "node_modules/@vitest/mocker": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.1.tgz", + "integrity": "sha512-LNN5VwOEdJqCmJ/2XJBywB11DLlkbY0ooDJW3uRX5cZyYCrc4PI/ePX0iQhE3BiEGiQmK4GE7Q/PqCkkaiPnrA==", "dev": true, "dependencies": { - "@vitest/utils": "1.6.0", - "p-limit": "^5.0.0", - "pathe": "^1.1.1" + "@vitest/spy": "^2.1.0-beta.1", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.11" }, "funding": { "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@vitest/spy": "2.1.1", + "msw": "^2.3.5", + "vite": "^5.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } } }, - "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==", + "node_modules/@vitest/mocker/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": { - "yocto-queue": "^1.0.0" - }, - "engines": { - "node": ">=18" + "@types/estree": "^1.0.0" + } + }, + "node_modules/@vitest/pretty-format": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.1.tgz", + "integrity": "sha512-SjxPFOtuINDUW8/UkElJYQSFtnWX7tMksSGW0vfjxMneFqxVr8YJ979QpMbDW7g+BIiq88RAGDjf7en6rvLPPQ==", + "dev": true, + "dependencies": { + "tinyrainbow": "^1.2.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://opencollective.com/vitest" } }, - "node_modules/@vitest/runner/node_modules/yocto-queue": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.1.1.tgz", - "integrity": "sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==", + "node_modules/@vitest/runner": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.1.tgz", + "integrity": "sha512-uTPuY6PWOYitIkLPidaY5L3t0JJITdGTSwBtwMjKzo5O6RCOEncz9PUN+0pDidX8kTHYjO0EwUIvhlGpnGpxmA==", "dev": true, - "engines": { - "node": ">=12.20" + "dependencies": { + "@vitest/utils": "2.1.1", + "pathe": "^1.1.2" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/snapshot": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.6.0.tgz", - "integrity": "sha512-+Hx43f8Chus+DCmygqqfetcAZrDJwvTj0ymqjQq4CvmpKFSTVteEOBzCusu1x2tt4OJcvBflyHUE0DZSLgEMtQ==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.1.tgz", + "integrity": "sha512-BnSku1WFy7r4mm96ha2FzN99AZJgpZOWrAhtQfoxjUU5YMRpq1zmHRq7a5K9/NjqonebO7iVDla+VvZS8BOWMw==", "dev": true, "dependencies": { - "magic-string": "^0.30.5", - "pathe": "^1.1.1", - "pretty-format": "^29.7.0" + "@vitest/pretty-format": "2.1.1", + "magic-string": "^0.30.11", + "pathe": "^1.1.2" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/spy": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.6.0.tgz", - "integrity": "sha512-leUTap6B/cqi/bQkXUu6bQV5TZPx7pmMBKBQiI0rJA8c3pB56ZsaTbREnF7CJfmvAS4V2cXIBAh/3rVwrrCYgw==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.1.tgz", + "integrity": "sha512-ZM39BnZ9t/xZ/nF4UwRH5il0Sw93QnZXd9NAZGRpIgj0yvVwPpLd702s/Cx955rGaMlyBQkZJ2Ir7qyY48VZ+g==", "dev": true, "dependencies": { - "tinyspy": "^2.2.0" + "tinyspy": "^3.0.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/utils": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.6.0.tgz", - "integrity": "sha512-21cPiuGMoMZwiOHa2i4LXkMkMkCGzA+MVFV70jRwHo95dL4x/ts5GZhML1QWuy7yfp3WzK3lRvZi3JnXTYqrBw==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.1.tgz", + "integrity": "sha512-Y6Q9TsI+qJ2CC0ZKj6VBb+T8UPz593N113nnUykqwANqhgf3QkZeHFlusgKLTqrnVHbj/XDKZcDHol+dxVT+rQ==", "dev": true, "dependencies": { - "diff-sequences": "^29.6.3", - "estree-walker": "^3.0.3", - "loupe": "^2.3.7", - "pretty-format": "^29.7.0" + "@vitest/pretty-format": "2.1.1", + "loupe": "^3.1.1", + "tinyrainbow": "^1.2.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, - "node_modules/@vitest/utils/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/@yarnpkg/lockfile": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", @@ -3420,18 +3476,6 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/acorn-walk": { - "version": "8.3.3", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.3.tgz", - "integrity": "sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==", - "dev": true, - "dependencies": { - "acorn": "^8.11.0" - }, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/add-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/add-stream/-/add-stream-1.0.0.tgz", @@ -3503,18 +3547,6 @@ "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==", - "dev": true, - "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", @@ -3524,17 +3556,15 @@ } }, "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==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "color-convert": "^1.9.0" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=4" } }, "node_modules/aproba": { @@ -3549,22 +3579,6 @@ "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-differ": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-3.0.0.tgz", @@ -3594,28 +3608,6 @@ "node": ">=8" } }, - "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/arrify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", @@ -3626,18 +3618,18 @@ } }, "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==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", "dev": true, "engines": { - "node": "*" + "node": ">=12" } }, "node_modules/async": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", - "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", "dev": true }, "node_modules/asynckit": { @@ -3647,15 +3639,16 @@ "dev": true }, "node_modules/auto-changelog": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/auto-changelog/-/auto-changelog-2.4.0.tgz", - "integrity": "sha512-vh17hko1c0ItsEcw6m7qPRf3m45u+XK5QyCrrBFViElZ8jnKrPC1roSznrd1fIB/0vR/zawdECCRJtTuqIXaJw==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/auto-changelog/-/auto-changelog-2.5.0.tgz", + "integrity": "sha512-UTnLjT7I9U2U/xkCUH5buDlp8C7g0SGChfib+iDrJkamcj5kaMqNKHNfbKJw1kthJUq8sUo3i3q2S6FzO/l/wA==", "dev": true, "dependencies": { "commander": "^7.2.0", "handlebars": "^4.7.7", + "import-cwd": "^3.0.0", "node-fetch": "^2.6.1", - "parse-github-url": "^1.0.2", + "parse-github-url": "^1.0.3", "semver": "^7.3.5" }, "bin": { @@ -3665,25 +3658,10 @@ "node": ">=8.3" } }, - "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==", - "dev": true, - "dependencies": { - "possible-typed-array-names": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/axios": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz", - "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==", + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", + "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", "dev": true, "dependencies": { "follow-redirects": "^1.15.6", @@ -3749,9 +3727,9 @@ } }, "node_modules/body-parser": { - "version": "1.20.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", - "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", "dependencies": { "bytes": "3.1.2", "content-type": "~1.0.5", @@ -3761,7 +3739,7 @@ "http-errors": "2.0.0", "iconv-lite": "0.4.24", "on-finished": "2.4.1", - "qs": "6.11.0", + "qs": "6.13.0", "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" @@ -3805,9 +3783,9 @@ } }, "node_modules/browserslist": { - "version": "4.23.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.2.tgz", - "integrity": "sha512-qkqSyistMYdxAcw+CzbZwlBy8AGmS/eEWs+sEV5TnLRGDOL+C5M2EnH6tlZyg0YoAxGJAFKh61En9BR941GnHA==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.0.tgz", + "integrity": "sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A==", "dev": true, "funding": [ { @@ -3824,9 +3802,9 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001640", - "electron-to-chromium": "^1.4.820", - "node-releases": "^2.0.14", + "caniuse-lite": "^1.0.30001663", + "electron-to-chromium": "^1.5.28", + "node-releases": "^2.0.18", "update-browserslist-db": "^1.1.0" }, "bin": { @@ -3874,18 +3852,6 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, - "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, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/byte-size": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/byte-size/-/byte-size-8.1.1.tgz", @@ -3935,6 +3901,12 @@ "node": "^16.14.0 || >=18.0.0" } }, + "node_modules/cacache/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 + }, "node_modules/call-bind": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", @@ -3989,9 +3961,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001646", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001646.tgz", - "integrity": "sha512-dRg00gudiBDDTmUhClSdv3hqRfpbOnU28IpI1T6PBTLWa+kOj0681C8uML3PifYfREuBrVjDGhL3adYpBT6spw==", + "version": "1.0.30001666", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001666.tgz", + "integrity": "sha512-gD14ICmoV5ZZM1OdzPWmpx+q4GyefaK06zi8hmfHV5xe4/2nOQX3+Dw5o+fSqOws2xVwL9j+anOPFwHzdEdV4g==", "dev": true, "funding": [ { @@ -4009,37 +3981,33 @@ ] }, "node_modules/chai": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz", - "integrity": "sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.1.tgz", + "integrity": "sha512-pT1ZgP8rPNqUgieVaEY+ryQr6Q4HXNg8Ei9UnLUrjN4IA7dvQC5JB+/kxVcPNDHyBcc/26CXPkbNzq3qwrOEKA==", "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.1.0" + "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": ">=4" + "node": ">=12" } }, "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">=4" } }, "node_modules/chardet": { @@ -4049,15 +4017,12 @@ "dev": true }, "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==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", + "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", "dev": true, - "dependencies": { - "get-func-name": "^2.0.2" - }, "engines": { - "node": "*" + "node": ">= 16" } }, "node_modules/chownr": { @@ -4130,6 +4095,7 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", @@ -4139,10 +4105,44 @@ "node": ">=12" } }, + "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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/cliui/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/cliui/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, "node_modules/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==", + "dev": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -4200,20 +4200,19 @@ } }, "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==", + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" + "color-name": "1.1.3" } }, "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==" + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true }, "node_modules/color-support": { "version": "1.1.3", @@ -4295,12 +4294,6 @@ "typedarray": "^0.0.6" } }, - "node_modules/confbox": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.7.tgz", - "integrity": "sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==", - "dev": true - }, "node_modules/console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", @@ -4399,6 +4392,12 @@ "node": ">=10" } }, + "node_modules/conventional-changelog-core/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/conventional-changelog-preset-loader": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-3.0.0.tgz", @@ -4616,88 +4615,37 @@ "node": ">=8" } }, - "node_modules/data-view-buffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", - "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", + "node_modules/dateformat": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", + "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==", "dev": true, - "dependencies": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" - }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": "*" } }, - "node_modules/data-view-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", - "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", + "node_modules/debounce": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/debounce/-/debounce-2.1.1.tgz", + "integrity": "sha512-+xRWxgel9LgTC4PwKlm7TJUK6B6qsEK77NaiNvXmeQ7Y3e6OVVsBC4a9BSptS/mAYceyAz37Oa8JTTuPRft7uQ==", "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" - }, "engines": { - "node": ">= 0.4" + "node": ">=18" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/data-view-byte-offset": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", - "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", + "node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" + "ms": "^2.1.3" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/dateformat": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", - "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/debounce": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/debounce/-/debounce-2.1.0.tgz", - "integrity": "sha512-OkL3+0pPWCqoBc/nhO9u6TIQNTK44fnBnzuVtJAbp13Naxw9R6u21x+8tVTka87AhDZ3htqZ2pSSsZl9fqL2Wg==", - "dev": true, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/debug": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", - "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" + "node": ">=6.0" }, "peerDependenciesMeta": { "supports-color": { @@ -4754,13 +4702,10 @@ } }, "node_modules/deep-eql": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", - "integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==", + "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, - "dependencies": { - "type-detect": "^4.0.0" - }, "engines": { "node": ">=6" } @@ -4817,23 +4762,6 @@ "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/degit": { "version": "2.8.4", "resolved": "https://registry.npmjs.org/degit/-/degit-2.8.4.tgz", @@ -4936,7 +4864,6 @@ "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" }, @@ -4991,9 +4918,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.4.tgz", - "integrity": "sha512-orzA81VqLyIGUEA77YkVA1D+N+nNfl2isJVjjmOyrlxuooZ19ynb+dOlaDTqd/idKRS9lDCSBmtzM+kyCsMnkA==", + "version": "1.5.31", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.31.tgz", + "integrity": "sha512-QcDoBbQeYt0+3CWcK/rEbuHvwpbT/8SV9T3OSgs6cX1FlcUAkgrkqbg9zLnDrMM/rLamzQwal4LYFCiWk861Tg==", "dev": true }, "node_modules/emoji-regex": { @@ -5002,9 +4929,9 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", "engines": { "node": ">= 0.8" } @@ -5089,66 +5016,6 @@ "is-arrayish": "^0.2.1" } }, - "node_modules/es-abstract": { - "version": "1.23.3", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", - "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", - "dev": true, - "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "arraybuffer.prototype.slice": "^1.0.3", - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "data-view-buffer": "^1.0.1", - "data-view-byte-length": "^1.0.1", - "data-view-byte-offset": "^1.0.0", - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-set-tostringtag": "^2.0.3", - "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.3", - "has-symbols": "^1.0.3", - "hasown": "^2.0.2", - "internal-slot": "^1.0.7", - "is-array-buffer": "^3.0.4", - "is-callable": "^1.2.7", - "is-data-view": "^1.0.1", - "is-negative-zero": "^2.0.3", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.3", - "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.2", - "safe-regex-test": "^1.0.3", - "string.prototype.trim": "^1.2.9", - "string.prototype.trimend": "^1.0.8", - "string.prototype.trimstart": "^1.0.8", - "typed-array-buffer": "^1.0.2", - "typed-array-byte-length": "^1.0.1", - "typed-array-byte-offset": "^1.0.2", - "typed-array-length": "^1.0.6", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.15" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/es-define-property": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", @@ -5168,54 +5035,6 @@ "node": ">= 0.4" } }, - "node_modules/es-object-atoms": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", - "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", - "dev": true, - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "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-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/es6-promise": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", - "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" - }, "node_modules/esbuild": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", @@ -5255,9 +5074,10 @@ } }, "node_modules/escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, "engines": { "node": ">=6" } @@ -5268,28 +5088,25 @@ "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==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.8.0" } }, "node_modules/eslint": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", - "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", "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", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "@ungap/structured-clone": "^1.2.0", @@ -5378,6 +5195,21 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/eslint/node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -5388,6 +5220,76 @@ "concat-map": "0.0.1" } }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/eslint/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/eslint/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/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/eslint/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/eslint/node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -5400,6 +5302,30 @@ "node": "*" } }, + "node_modules/eslint/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/espree": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", @@ -5522,36 +5448,36 @@ "dev": true }, "node_modules/express": { - "version": "4.19.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", - "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", + "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.2", + "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", "cookie": "0.6.0", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "1.2.0", + "finalhandler": "1.3.1", "fresh": "0.5.2", "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", + "merge-descriptors": "1.0.3", "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", + "path-to-regexp": "0.1.10", "proxy-addr": "~2.0.7", - "qs": "6.11.0", + "qs": "6.13.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", + "send": "0.19.0", + "serve-static": "1.16.2", "setprototypeof": "1.2.0", "statuses": "2.0.1", "type-is": "~1.6.18", @@ -5562,27 +5488,6 @@ "node": ">= 0.10.0" } }, - "node_modules/express-http-proxy": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/express-http-proxy/-/express-http-proxy-2.0.0.tgz", - "integrity": "sha512-TXxcPFTWVUMSEmyM6iX2sT/JtmqhqngTq29P+eXTVFdtxZrTmM8THUYK59rUXiln0FfPGvxEpGRnVrgvHksXDw==", - "dependencies": { - "debug": "^3.0.1", - "es6-promise": "^4.1.1", - "raw-body": "^2.3.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/express-http-proxy/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dependencies": { - "ms": "^2.1.1" - } - }, "node_modules/express/node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -5692,15 +5597,6 @@ "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==", - "dev": true, - "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", @@ -5747,12 +5643,12 @@ } }, "node_modules/finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", "dependencies": { "debug": "2.6.9", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "on-finished": "2.4.1", "parseurl": "~1.3.3", @@ -5828,9 +5724,9 @@ "dev": true }, "node_modules/follow-redirects": { - "version": "1.15.6", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", - "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", "dev": true, "funding": [ { @@ -5847,19 +5743,10 @@ } } }, - "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "dev": true, - "dependencies": { - "is-callable": "^1.1.3" - } - }, "node_modules/foreground-child": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.2.1.tgz", - "integrity": "sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", "dependencies": { "cross-spawn": "^7.0.0", "signal-exit": "^4.0.1" @@ -6008,33 +5895,6 @@ "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/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -6048,6 +5908,7 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, "engines": { "node": "6.* || 8.* || >= 10.*" } @@ -6097,17 +5958,50 @@ "node": ">=6.9.0" } }, - "node_modules/get-pkg-repo/node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "node_modules/get-pkg-repo/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "dependencies": { - "string-width": "^4.2.0", + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/get-pkg-repo/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^7.0.0" } }, + "node_modules/get-pkg-repo/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/get-pkg-repo/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, "node_modules/get-pkg-repo/node_modules/hosted-git-info": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", @@ -6149,6 +6043,12 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/get-pkg-repo/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/get-pkg-repo/node_modules/yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", @@ -6200,23 +6100,6 @@ "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/git-raw-commits": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-3.0.0.tgz", @@ -6345,34 +6228,12 @@ } }, "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.4", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", - "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true, - "dependencies": { - "define-properties": "^1.2.1", - "gopd": "^1.0.1" - }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=4" } }, "node_modules/globby": { @@ -6447,22 +6308,13 @@ "node": ">=6" } }, - "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==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, "engines": { - "node": ">=8" + "node": ">=4" } }, "node_modules/has-property-descriptors": { @@ -6498,21 +6350,6 @@ "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==", - "dev": true, - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/has-unicode": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", @@ -6542,6 +6379,12 @@ "node": "^16.14.0 || >=18.0.0" } }, + "node_modules/hosted-git-info/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 + }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -6636,9 +6479,9 @@ ] }, "node_modules/ignore": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", - "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, "engines": { "node": ">= 4" @@ -6656,6 +6499,18 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/import-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-3.0.0.tgz", + "integrity": "sha512-4pnzH16plW+hgvRECbDWpQl3cqtvSofHWh44met7ESfZ8UZOWWddm8hEyDTqREJ9RbYHY8gi8DqmaelApoOGMg==", + "dev": true, + "dependencies": { + "import-from": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -6672,6 +6527,27 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/import-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/import-from/-/import-from-3.0.0.tgz", + "integrity": "sha512-CiuXOFFSzkU5x/CR0+z7T91Iht4CXgfCxVOFRhh2Zyhg5wOpWvvDLQUsWl+gcN+QscYBjez8hDCt85O7RLDttQ==", + "dev": true, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/import-from/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/import-local": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", @@ -6775,118 +6651,103 @@ "node": ">=12.0.0" } }, - "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==", + "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==", "dev": true, "dependencies": { - "es-errors": "^1.3.0", - "hasown": "^2.0.0", - "side-channel": "^1.0.4" + "color-convert": "^2.0.1" }, "engines": { - "node": ">= 0.4" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/ip-address": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", - "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", - "devOptional": true, + "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==", + "dev": true, "dependencies": { - "jsbn": "1.1.0", - "sprintf-js": "^1.1.3" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">= 12" - } - }, - "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": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "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==", + "node_modules/inquirer/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1" + "color-name": "~1.1.4" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=7.0.0" } }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "node_modules/inquirer/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "node_modules/inquirer/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "dependencies": { - "has-bigints": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "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==", + "node_modules/inquirer/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "has-flag": "^4.0.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, - "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, + "node_modules/ip-address": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", + "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "devOptional": true, "dependencies": { - "builtin-modules": "^3.3.0" + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" }, "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 12" } }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "dev": true, + "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.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 0.10" } }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, "node_modules/is-ci": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", @@ -6900,9 +6761,9 @@ } }, "node_modules/is-core-module": { - "version": "2.15.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.0.tgz", - "integrity": "sha512-Dd+Lb2/zvk9SKy1TGCt1wFJFo/MWBPMX5x7KcvLajWTGuomczdQX61PvY5yK6SVACwpoexWo81IfFyoKY2QnTA==", + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", + "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", "dev": true, "dependencies": { "hasown": "^2.0.2" @@ -6914,55 +6775,25 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-data-view": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", - "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", + "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==", "dev": true, - "dependencies": { - "is-typed-array": "^1.1.13" + "bin": { + "is-docker": "cli.js" }, "engines": { - "node": ">= 0.4" + "node": ">=8" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", - "dev": true, - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, "engines": { "node": ">=0.10.0" @@ -7009,18 +6840,6 @@ "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", "dev": true }, - "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", @@ -7030,21 +6849,6 @@ "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-obj": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", @@ -7081,37 +6885,6 @@ "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-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-ssh": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/is-ssh/-/is-ssh-1.4.0.tgz", @@ -7130,36 +6903,6 @@ "node": ">=8" } }, - "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-text-path": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-1.0.1.tgz", @@ -7172,21 +6915,6 @@ "node": ">=0.10.0" } }, - "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==", - "dev": true, - "dependencies": { - "which-typed-array": "^1.1.14" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-unicode-supported": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", @@ -7199,18 +6927,6 @@ "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-wsl": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", @@ -7266,6 +6982,27 @@ "node": ">=10" } }, + "node_modules/istanbul-lib-report/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/istanbul-lib-source-maps": { "version": "5.0.6", "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", @@ -7297,6 +7034,7 @@ "version": "3.4.3", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, "dependencies": { "@isaacs/cliui": "^8.0.2" }, @@ -7325,6 +7063,21 @@ "node": ">=10" } }, + "node_modules/jake/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/jake/node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -7335,6 +7088,49 @@ "concat-map": "0.0.1" } }, + "node_modules/jake/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/jake/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jake/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/jake/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/jake/node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -7347,6 +7143,18 @@ "node": "*" } }, + "node_modules/jake/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/jest-diff": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", @@ -7362,6 +7170,76 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/jest-diff/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-diff/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-diff/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-diff/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/jest-diff/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-diff/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/jest-get-type": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", @@ -7419,16 +7297,8 @@ "resolved": "examples/concepts/multi-version", "link": true }, - "node_modules/jitar-node-client-example": { - "resolved": "examples/concepts/node-client", - "link": true - }, - "node_modules/jitar-overrides-example": { - "resolved": "examples/concepts/overrides", - "link": true - }, - "node_modules/jitar-segmentation-example": { - "resolved": "examples/concepts/segmentation", + "node_modules/jitar-segmentation-example": { + "resolved": "examples/concepts/segmentation", "link": true }, "node_modules/js-tokens": { @@ -7612,13 +7482,13 @@ "integrity": "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==" }, "node_modules/lerna": { - "version": "8.1.7", - "resolved": "https://registry.npmjs.org/lerna/-/lerna-8.1.7.tgz", - "integrity": "sha512-v2kkBn8Vqtroo30Pr5/JQ9MygRhnCsoI1jSOf3DxWmcTbkpC5U7c6rGr+7NPK6QrxKbC0/Cj4kuIBMb/7f79sQ==", + "version": "8.1.8", + "resolved": "https://registry.npmjs.org/lerna/-/lerna-8.1.8.tgz", + "integrity": "sha512-Rmo5ShMx73xM2CUcRixjmpZIXB7ZFlWEul1YvJyx/rH4onAwDHtUGD7Rx4NZYL8QSRiQHroglM2Oyq+WqA4BYg==", "dev": true, "dependencies": { - "@lerna/create": "8.1.7", - "@npmcli/arborist": "7.5.3", + "@lerna/create": "8.1.8", + "@npmcli/arborist": "7.5.4", "@npmcli/package-json": "5.2.0", "@npmcli/run-script": "8.1.0", "@nx/devkit": ">=17.1.2 < 20", @@ -7706,6 +7576,21 @@ "node": ">=18.0.0" } }, + "node_modules/lerna/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/lerna/node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -7732,6 +7617,24 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/lerna/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/lerna/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, "node_modules/lerna/node_modules/glob": { "version": "9.3.5", "resolved": "https://registry.npmjs.org/glob/-/glob-9.3.5.tgz", @@ -7774,6 +7677,15 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/lerna/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/lerna/node_modules/minimatch": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.5.tgz", @@ -7842,6 +7754,40 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/lerna/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lerna/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true + }, + "node_modules/lerna/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==", + "dev": true + }, + "node_modules/lerna/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==", + "dev": true, + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -7903,9 +7849,9 @@ } }, "node_modules/lines-and-columns": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-2.0.4.tgz", - "integrity": "sha512-wM1+Z03eypVAVUCE7QdSqpVIvelbOakn1M0bPDoA4SGWPx3sNDVUiMo3L6To6WWGClB7VyXnhQ4Sn7gxiJbE6A==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-2.0.3.tgz", + "integrity": "sha512-cNOjgCnLB+FnvWWtyRTzmB3POJ+cXxTA81LoW7u8JdmhfXzriropYwpjShnz1QLLWsQwY7nIxoDmcPTwphDK9w==", "dev": true, "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" @@ -7935,22 +7881,6 @@ "node": ">=8" } }, - "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", @@ -8000,6 +7930,76 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/log-symbols/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/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==", + "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/log-symbols/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/log-symbols/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/log-symbols/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/log-symbols/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -8012,18 +8012,22 @@ } }, "node_modules/loupe": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", - "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.1.tgz", + "integrity": "sha512-edNu/8D5MKVfGVFRhFf8aAxiTM6Wumfz5XsaatSxlD3w4R1d/WEKUTydCdPGbl9K7QG/Ca3GnDV2sIKIpXRQcw==", "dev": true, "dependencies": { "get-func-name": "^2.0.1" } }, "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==" + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } }, "node_modules/magic-string": { "version": "0.30.11", @@ -8035,13 +8039,13 @@ } }, "node_modules/magicast": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.4.tgz", - "integrity": "sha512-TyDF/Pn36bBji9rWKHlZe+PZb6Mx5V8IHCSxk7X4aljM4e/vyDvZZYwHewdVaqiA0nb3ghfHU/6AUpDxWoER2Q==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.5.tgz", + "integrity": "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==", "dev": true, "dependencies": { - "@babel/parser": "^7.24.4", - "@babel/types": "^7.24.0", + "@babel/parser": "^7.25.4", + "@babel/types": "^7.25.4", "source-map-js": "^1.2.0" } }, @@ -8108,15 +8112,6 @@ "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==" }, - "node_modules/memorystream": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", - "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", - "dev": true, - "engines": { - "node": ">= 0.10.0" - } - }, "node_modules/meow": { "version": "8.1.2", "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", @@ -8322,6 +8317,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/meow/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/meow/node_modules/yargs-parser": { "version": "20.2.9", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", @@ -8332,9 +8333,12 @@ } }, "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/merge-stream": { "version": "2.0.0", @@ -8360,9 +8364,9 @@ } }, "node_modules/micromatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", - "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "dependencies": { "braces": "^3.0.3", @@ -8424,6 +8428,7 @@ "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, "dependencies": { "brace-expansion": "^2.0.1" }, @@ -8517,6 +8522,12 @@ "node": ">=8" } }, + "node_modules/minipass-flush/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/minipass-pipeline": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", @@ -8541,6 +8552,12 @@ "node": ">=8" } }, + "node_modules/minipass-pipeline/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/minipass-sized": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", @@ -8565,6 +8582,12 @@ "node": ">=8" } }, + "node_modules/minipass-sized/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/minizlib": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", @@ -8590,6 +8613,12 @@ "node": ">=8" } }, + "node_modules/minizlib/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", @@ -8602,18 +8631,6 @@ "node": ">=10" } }, - "node_modules/mlly": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.1.tgz", - "integrity": "sha512-rrVRZRELyQzrIUAVMHxP97kv+G786pHmOKzuFII8zDYahFBS7qnHh2AlYSl1GAHhaMPCz6/oHjVMcfFYgFYHgA==", - "dev": true, - "dependencies": { - "acorn": "^8.11.3", - "pathe": "^1.1.2", - "pkg-types": "^1.1.1", - "ufo": "^1.5.3" - } - }, "node_modules/modify-values": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz", @@ -8624,9 +8641,9 @@ } }, "node_modules/mongodb": { - "version": "6.8.0", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.8.0.tgz", - "integrity": "sha512-HGQ9NWDle5WvwMnrvUxsFYPd3JEbqD3RgABHBQRuoCEND0qzhsd0iH5ypHsf1eJ+sXmvmyKpP+FLOKY8Il7jMw==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.9.0.tgz", + "integrity": "sha512-UMopBVx1LmEUbW/QE0Hw18u583PEDVQmUmVzzBRH0o/xtE9DBRA5ZYLOjpLIa03i8FXjzvQECJcqoMvCXftTUA==", "dependencies": { "@mongodb-js/saslprep": "^1.1.5", "bson": "^6.7.0", @@ -8677,41 +8694,10 @@ "whatwg-url": "^13.0.0" } }, - "node_modules/mongodb-connection-string-url/node_modules/tr46": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz", - "integrity": "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==", - "dependencies": { - "punycode": "^2.3.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/mongodb-connection-string-url/node_modules/webidl-conversions": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", - "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", - "engines": { - "node": ">=12" - } - }, - "node_modules/mongodb-connection-string-url/node_modules/whatwg-url": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-13.0.0.tgz", - "integrity": "sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==", - "dependencies": { - "tr46": "^4.1.1", - "webidl-conversions": "^7.0.0" - }, - "engines": { - "node": ">=16" - } - }, "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==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/multimatch": { "version": "5.0.0", @@ -8807,12 +8793,6 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, - "node_modules/nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true - }, "node_modules/node-fetch": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", @@ -8833,6 +8813,28 @@ } } }, + "node_modules/node-fetch/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true + }, + "node_modules/node-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true + }, + "node_modules/node-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/node-gyp": { "version": "10.2.0", "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-10.2.0.tgz", @@ -9016,191 +9018,6 @@ "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/npm-run-all": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", - "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "chalk": "^2.4.1", - "cross-spawn": "^6.0.5", - "memorystream": "^0.3.1", - "minimatch": "^3.0.4", - "pidtree": "^0.3.0", - "read-pkg": "^3.0.0", - "shell-quote": "^1.6.1", - "string.prototype.padend": "^3.0.0" - }, - "bin": { - "npm-run-all": "bin/npm-run-all/index.js", - "run-p": "bin/run-p/index.js", - "run-s": "bin/run-s/index.js" - }, - "engines": { - "node": ">= 4" - } - }, - "node_modules/npm-run-all/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/npm-run-all/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/npm-run-all/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/npm-run-all/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/npm-run-all/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/npm-run-all/node_modules/cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "dependencies": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - }, - "engines": { - "node": ">=4.8" - } - }, - "node_modules/npm-run-all/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/npm-run-all/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/npm-run-all/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/npm-run-all/node_modules/path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/npm-run-all/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/npm-run-all/node_modules/shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", - "dev": true, - "dependencies": { - "shebang-regex": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm-run-all/node_modules/shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm-run-all/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/npm-run-all/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, "node_modules/npm-run-path": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", @@ -9214,18 +9031,18 @@ } }, "node_modules/nx": { - "version": "19.5.4", - "resolved": "https://registry.npmjs.org/nx/-/nx-19.5.4.tgz", - "integrity": "sha512-zfxIFe+29Na6GKlmPPzQhCjnBv5HoLaT43mYZdHh3BPrVOzWBCXNwxWROG1ZK9IcUepwySWq7NI/H3w8BGPEGg==", + "version": "19.8.3", + "resolved": "https://registry.npmjs.org/nx/-/nx-19.8.3.tgz", + "integrity": "sha512-/3FF4tgwPGRu4bV6O+aHqhTnOGHKF0/HNVkApUwjimSC+YzOX9VH1uBx2eReb4XC1scxDWkIzVi9gkFSXSQDjQ==", "dev": true, "hasInstallScript": true, "dependencies": { "@napi-rs/wasm-runtime": "0.2.4", - "@nrwl/tao": "19.5.4", + "@nrwl/tao": "19.8.3", "@yarnpkg/lockfile": "^1.1.0", "@yarnpkg/parsers": "3.0.0-rc.46", "@zkochan/js-yaml": "0.0.7", - "axios": "^1.7.2", + "axios": "^1.7.4", "chalk": "^4.1.0", "cli-cursor": "3.1.0", "cli-spinners": "2.6.1", @@ -9236,11 +9053,10 @@ "figures": "3.2.0", "flat": "^5.0.2", "front-matter": "^4.0.2", - "fs-extra": "^11.1.0", "ignore": "^5.0.4", "jest-diff": "^29.4.1", "jsonc-parser": "3.2.0", - "lines-and-columns": "~2.0.3", + "lines-and-columns": "2.0.3", "minimatch": "9.0.3", "node-machine-id": "1.1.12", "npm-run-path": "^4.0.1", @@ -9261,16 +9077,16 @@ "nx-cloud": "bin/nx-cloud.js" }, "optionalDependencies": { - "@nx/nx-darwin-arm64": "19.5.4", - "@nx/nx-darwin-x64": "19.5.4", - "@nx/nx-freebsd-x64": "19.5.4", - "@nx/nx-linux-arm-gnueabihf": "19.5.4", - "@nx/nx-linux-arm64-gnu": "19.5.4", - "@nx/nx-linux-arm64-musl": "19.5.4", - "@nx/nx-linux-x64-gnu": "19.5.4", - "@nx/nx-linux-x64-musl": "19.5.4", - "@nx/nx-win32-arm64-msvc": "19.5.4", - "@nx/nx-win32-x64-msvc": "19.5.4" + "@nx/nx-darwin-arm64": "19.8.3", + "@nx/nx-darwin-x64": "19.8.3", + "@nx/nx-freebsd-x64": "19.8.3", + "@nx/nx-linux-arm-gnueabihf": "19.8.3", + "@nx/nx-linux-arm64-gnu": "19.8.3", + "@nx/nx-linux-arm64-musl": "19.8.3", + "@nx/nx-linux-x64-gnu": "19.8.3", + "@nx/nx-linux-x64-musl": "19.8.3", + "@nx/nx-win32-arm64-msvc": "19.8.3", + "@nx/nx-win32-x64-msvc": "19.8.3" }, "peerDependencies": { "@swc-node/register": "^1.8.0", @@ -9285,6 +9101,64 @@ } } }, + "node_modules/nx/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/nx/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/nx/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/nx/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/nx/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/nx/node_modules/minimatch": { "version": "9.0.3", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", @@ -9322,37 +9196,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/object-inspect": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", - "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", - "engines": { - "node": ">= 0.4" - }, - "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==", + "node_modules/nx/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, "engines": { - "node": ">= 0.4" + "node": ">=8" } }, - "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" - }, + "node_modules/object-inspect": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", + "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", "engines": { "node": ">= 0.4" }, @@ -9412,44 +9271,114 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "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==", + "dev": true, + "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/ora/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/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==", + "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/ora/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/ora/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/ora/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.5" - }, "engines": { - "node": ">= 0.8.0" + "node": ">=8" } }, - "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==", + "node_modules/ora/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "dependencies": { - "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" + "has-flag": "^4.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, "node_modules/os-tmpdir": { @@ -9598,9 +9527,9 @@ } }, "node_modules/package-json-from-dist": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", - "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==" + "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==" }, "node_modules/pacote": { "version": "18.0.6", @@ -9763,6 +9692,7 @@ "version": "1.11.1", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" @@ -9774,10 +9704,16 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/path-scurry/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 + }, "node_modules/path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", + "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==" }, "node_modules/path-type": { "version": "4.0.0", @@ -9795,18 +9731,18 @@ "dev": true }, "node_modules/pathval": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", - "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz", + "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==", "dev": true, "engines": { - "node": "*" + "node": ">= 14.16" } }, "node_modules/picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", + "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==", "devOptional": true }, "node_modules/picomatch": { @@ -9821,18 +9757,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/pidtree": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", - "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==", - "dev": true, - "bin": { - "pidtree": "bin/pidtree.js" - }, - "engines": { - "node": ">=0.10" - } - }, "node_modules/pify": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-5.0.0.tgz", @@ -9909,30 +9833,10 @@ "node": ">=8" } }, - "node_modules/pkg-types": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.1.3.tgz", - "integrity": "sha512-+JrgthZG6m3ckicaOB74TwQ+tBWsFl3qVQg7mN8ulwSOElJ7gBhKzj2VkCPnZ4NlF6kEquYU+RIYNVAvzd54UA==", - "dev": true, - "dependencies": { - "confbox": "^0.1.7", - "mlly": "^1.7.1", - "pathe": "^1.1.2" - } - }, - "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==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/postcss": { - "version": "8.4.40", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.40.tgz", - "integrity": "sha512-YF2kKIUzAofPMpfH6hOi2cGnv/HrUlfucspc7pDyvv7kGdqXrfj8SCl/t8owkEgKEuu8ZcRjSOxFxVLqwChZ2Q==", + "version": "8.4.47", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", + "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", "devOptional": true, "funding": [ { @@ -9950,17 +9854,17 @@ ], "dependencies": { "nanoid": "^3.3.7", - "picocolors": "^1.0.1", - "source-map-js": "^1.2.0" + "picocolors": "^1.1.0", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" } }, "node_modules/postcss-selector-parser": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.1.tgz", - "integrity": "sha512-b4dlw/9V8A71rLIDsSwVmak9z2DuBUB7CA1/wSdelNEzqsjoSPeADTWNO09lpH49Diy3/JIZ2bSPB1dI3LJCHg==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", "dev": true, "dependencies": { "cssesc": "^3.0.0", @@ -10039,9 +9943,9 @@ } }, "node_modules/promise-call-limit": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/promise-call-limit/-/promise-call-limit-3.0.1.tgz", - "integrity": "sha512-utl+0x8gIDasV5X+PI5qWEPqH6fJS0pFtQ/4gZ95xfEFb/89dmh+/b895TbFDBLiafBvxD/PGTKfvxl4kH/pQg==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/promise-call-limit/-/promise-call-limit-3.0.2.tgz", + "integrity": "sha512-mRPQO2T1QQVw11E7+UdCJu7S61eJVWknzml9sC1heAdj1jxl0fWMBypIt9ZOcLFf8FkG995ZD7RnVk7HH72fZw==", "dev": true, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -10123,11 +10027,11 @@ } }, "node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", "dependencies": { - "side-channel": "^1.0.4" + "side-channel": "^1.0.6" }, "engines": { "node": ">=0.6" @@ -10483,28 +10387,11 @@ "node": ">=8" } }, - "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/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -10648,12 +10535,12 @@ } }, "node_modules/rollup": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.19.1.tgz", - "integrity": "sha512-K5vziVlg7hTpYfFBI+91zHBEMo6jafYXpkMlqZjg7/zhIG9iHqazBf4xz9AVdjS9BruRn280ROqLI7G3OFRIlw==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.0.tgz", + "integrity": "sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg==", "devOptional": true, "dependencies": { - "@types/estree": "1.0.5" + "@types/estree": "1.0.6" }, "bin": { "rollup": "dist/bin/rollup" @@ -10663,22 +10550,22 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.19.1", - "@rollup/rollup-android-arm64": "4.19.1", - "@rollup/rollup-darwin-arm64": "4.19.1", - "@rollup/rollup-darwin-x64": "4.19.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.19.1", - "@rollup/rollup-linux-arm-musleabihf": "4.19.1", - "@rollup/rollup-linux-arm64-gnu": "4.19.1", - "@rollup/rollup-linux-arm64-musl": "4.19.1", - "@rollup/rollup-linux-powerpc64le-gnu": "4.19.1", - "@rollup/rollup-linux-riscv64-gnu": "4.19.1", - "@rollup/rollup-linux-s390x-gnu": "4.19.1", - "@rollup/rollup-linux-x64-gnu": "4.19.1", - "@rollup/rollup-linux-x64-musl": "4.19.1", - "@rollup/rollup-win32-arm64-msvc": "4.19.1", - "@rollup/rollup-win32-ia32-msvc": "4.19.1", - "@rollup/rollup-win32-x64-msvc": "4.19.1", + "@rollup/rollup-android-arm-eabi": "4.24.0", + "@rollup/rollup-android-arm64": "4.24.0", + "@rollup/rollup-darwin-arm64": "4.24.0", + "@rollup/rollup-darwin-x64": "4.24.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.24.0", + "@rollup/rollup-linux-arm-musleabihf": "4.24.0", + "@rollup/rollup-linux-arm64-gnu": "4.24.0", + "@rollup/rollup-linux-arm64-musl": "4.24.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.24.0", + "@rollup/rollup-linux-riscv64-gnu": "4.24.0", + "@rollup/rollup-linux-s390x-gnu": "4.24.0", + "@rollup/rollup-linux-x64-gnu": "4.24.0", + "@rollup/rollup-linux-x64-musl": "4.24.0", + "@rollup/rollup-win32-arm64-msvc": "4.24.0", + "@rollup/rollup-win32-ia32-msvc": "4.24.0", + "@rollup/rollup-win32-x64-msvc": "4.24.0", "fsevents": "~2.3.2" } }, @@ -10745,30 +10632,6 @@ "tslib": "^2.1.0" } }, - "node_modules/safe-array-concat": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", - "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "get-intrinsic": "^1.2.4", - "has-symbols": "^1.0.3", - "isarray": "^2.0.5" - }, - "engines": { - "node": ">=0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safe-array-concat/node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true - }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -10788,23 +10651,6 @@ } ] }, - "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/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -10831,9 +10677,9 @@ } }, "node_modules/send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", "dependencies": { "debug": "2.6.9", "depd": "2.0.0", @@ -10866,10 +10712,13 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, - "node_modules/send/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==" + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } }, "node_modules/serialize-javascript": { "version": "6.0.2", @@ -10881,14 +10730,14 @@ } }, "node_modules/serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", "dependencies": { - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.18.0" + "send": "0.19.0" }, "engines": { "node": ">= 0.8.0" @@ -10916,21 +10765,6 @@ "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", @@ -11102,9 +10936,9 @@ } }, "node_modules/source-map-js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", - "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "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==", "devOptional": true, "engines": { "node": ">=0.10.0" @@ -11155,9 +10989,9 @@ } }, "node_modules/spdx-license-ids": { - "version": "3.0.18", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.18.tgz", - "integrity": "sha512-xxRs31BqRYHwiMzudOrpSiHtZ8i/GeionCBDSilhYRj+9gIcI8wCZTlXZKu9vZIVqViP3dcp9qE5G6AlIaD+TQ==", + "version": "3.0.20", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.20.tgz", + "integrity": "sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw==", "dev": true }, "node_modules/split": { @@ -11255,73 +11089,6 @@ "node": ">=8" } }, - "node_modules/string.prototype.padend": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.6.tgz", - "integrity": "sha512-XZpspuSB7vJWhvJc9DLSlrXl1mcA2BdoY5jjnS135ydXqLoqhs96JjDtCkjJEQHvfqZIp9hBuBMgI589peyx9Q==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trim": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", - "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.0", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimend": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", - "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", - "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "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", @@ -11387,24 +11154,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/strip-literal": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-2.1.0.tgz", - "integrity": "sha512-Op+UycaUt/8FbN/Z2TWPBLge3jWrP3xj10f3fnYxf052bKuS3EKs1ZQcVGjnEMdsNVAM+plXRdmjrZ/KgG3Skw==", - "dev": true, - "dependencies": { - "js-tokens": "^9.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/strip-literal/node_modules/js-tokens": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.0.tgz", - "integrity": "sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ==", - "dev": true - }, "node_modules/strong-log-transformer": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/strong-log-transformer/-/strong-log-transformer-2.1.0.tgz", @@ -11432,15 +11181,15 @@ } }, "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==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "has-flag": "^3.0.0" }, "engines": { - "node": ">=8" + "node": ">=4" } }, "node_modules/supports-preserve-symlinks-flag": { @@ -11521,6 +11270,12 @@ "node": ">=8" } }, + "node_modules/tar/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/temp-dir": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-1.0.0.tgz", @@ -11531,84 +11286,41 @@ } }, "node_modules/terser": { - "version": "5.31.3", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.31.3.tgz", - "integrity": "sha512-pAfYn3NIZLyZpa83ZKigvj6Rn9c/vd5KfYGX7cN1mnzqgDcxWvrU5ZtAfIKhEXz9nRecw4z3LXkjaq96/qZqAA==", + "version": "5.34.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.34.1.tgz", + "integrity": "sha512-FsJZ7iZLd/BXkz+4xrRTGJ26o/6VTjQytUk8b8OxkwcD2I+79VPJlz7qss1+zE7h8GNIScFqXcDyJ/KqBYZFVA==", "dev": true, "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/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - }, - "node_modules/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==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "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" + "commander": "^2.20.0", + "source-map-support": "~0.5.20" }, - "engines": { - "node": "*" + "bin": { + "terser": "bin/terser" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "engines": { + "node": ">=10" } }, - "node_modules/test-exclude/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/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==", + "dev": true + }, + "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, "dependencies": { - "brace-expansion": "^1.1.7" + "@istanbuljs/schema": "^0.1.2", + "glob": "^10.4.1", + "minimatch": "^9.0.4" }, "engines": { - "node": "*" + "node": ">=18" } }, "node_modules/text-extensions": { @@ -11673,24 +11385,39 @@ } }, "node_modules/tinybench": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.8.0.tgz", - "integrity": "sha512-1/eK7zUnIklz4JUUlL+658n58XO2hHLQfSk1Zf2LKieUjxidN16eKFEoDEfjHc3ohofSSqK3X5yO6VGb6iW8Lw==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true + }, + "node_modules/tinyexec": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.0.tgz", + "integrity": "sha512-tVGE0mVJPGb0chKhqmsoosjsS+qUnJVGJpZgsHYQcGoPlG3B51R3PouqTgEGH2Dc9jjFyOqOpix6ZHNMXp1FZg==", "dev": true }, "node_modules/tinypool": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.4.tgz", - "integrity": "sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.1.tgz", + "integrity": "sha512-URZYihUbRPcGv95En+sz6MfghfIc2OJ1sv/RmhWZLouPY0/8Vo80viwPvg3dlaS9fuq7fQMEfgRRK7BBZThBEA==", + "dev": true, + "engines": { + "node": "^18.0.0 || >=20.0.0" + } + }, + "node_modules/tinyrainbow": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-1.2.0.tgz", + "integrity": "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==", "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==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", + "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", "dev": true, "engines": { "node": ">=14.0.0" @@ -11735,10 +11462,15 @@ } }, "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "dev": true + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz", + "integrity": "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==", + "dependencies": { + "punycode": "^2.3.0" + }, + "engines": { + "node": ">=14" + } }, "node_modules/treeverse": { "version": "3.0.0", @@ -11794,22 +11526,11 @@ } }, "node_modules/tslib": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", - "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", "dev": true }, - "node_modules/tslog": { - "version": "4.9.3", - "resolved": "https://registry.npmjs.org/tslog/-/tslog-4.9.3.tgz", - "integrity": "sha512-oDWuGVONxhVEBtschLf2cs/Jy8i7h1T+CpdkTNWQgdAF7DhRo2G8vMCgILKe7ojdEkLhICWgI1LYSSKaJsRgcw==", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/fullstack-build/tslog?sponsor=1" - } - }, "node_modules/tuf-js": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/tuf-js/-/tuf-js-2.2.1.tgz", @@ -11836,19 +11557,10 @@ "node": ">= 0.8.0" } }, - "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, - "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==", + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "dev": true, "engines": { "node": ">=10" @@ -11869,79 +11581,6 @@ "node": ">= 0.6" } }, - "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.6", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", - "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", - "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", @@ -11949,9 +11588,9 @@ "dev": true }, "node_modules/typescript": { - "version": "5.5.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", - "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", + "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -11961,16 +11600,10 @@ "node": ">=14.17" } }, - "node_modules/ufo": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.4.tgz", - "integrity": "sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==", - "dev": true - }, "node_modules/uglify-js": { - "version": "3.19.1", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.1.tgz", - "integrity": "sha512-y/2wiW+ceTYR2TSSptAhfnEtpLaQ4Ups5zrjB2d3kuVxHj16j/QJwPl5PvuGy9uARb39J0+iKxcRPvtpsx4A4A==", + "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==", "dev": true, "optional": true, "bin": { @@ -11980,25 +11613,10 @@ "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/undici-types": { - "version": "6.11.1", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.11.1.tgz", - "integrity": "sha512-mIDEX2ek50x0OlRgxryxsenE5XaQD4on5U2inY7RApK3SOJpofyw7uW2AyfMKkhAxXIceo2DeWGVGwyvng1GNQ==", + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", "dev": true }, "node_modules/unique-filename": { @@ -12058,9 +11676,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", - "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", + "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", "dev": true, "funding": [ { @@ -12077,8 +11695,8 @@ } ], "dependencies": { - "escalade": "^3.1.2", - "picocolors": "^1.0.1" + "escalade": "^3.2.0", + "picocolors": "^1.1.0" }, "bin": { "update-browserslist-db": "cli.js" @@ -12151,14 +11769,14 @@ } }, "node_modules/vite": { - "version": "5.3.5", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.3.5.tgz", - "integrity": "sha512-MdjglKR6AQXQb9JGiS7Rc2wC6uMjcm7Go/NHNO63EwiJXfuk9PgqiP/n5IDJCziMkfw9n4Ubp7lttNwz+8ZVKA==", + "version": "5.4.8", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.8.tgz", + "integrity": "sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==", "devOptional": true, "dependencies": { "esbuild": "^0.21.3", - "postcss": "^8.4.39", - "rollup": "^4.13.0" + "postcss": "^8.4.43", + "rollup": "^4.20.0" }, "bin": { "vite": "bin/vite.js" @@ -12177,6 +11795,7 @@ "less": "*", "lightningcss": "^1.21.0", "sass": "*", + "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" @@ -12194,6 +11813,9 @@ "sass": { "optional": true }, + "sass-embedded": { + "optional": true + }, "stylus": { "optional": true }, @@ -12206,15 +11828,14 @@ } }, "node_modules/vite-node": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.6.0.tgz", - "integrity": "sha512-de6HJgzC+TFzOu0NTC4RAIsyf/DY/ibWDYQUcuEA84EMHhcefTUGkjFHKKEJhQN4A+6I0u++kr3l36ZF2d7XRw==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.1.tgz", + "integrity": "sha512-N/mGckI1suG/5wQI35XeR9rsMsPqKXzq1CdUndzVstBj/HvyxxGctwnK6WX43NGt5L3Z5tcRf83g4TITKJhPrA==", "dev": true, "dependencies": { "cac": "^6.7.14", - "debug": "^4.3.4", - "pathe": "^1.1.1", - "picocolors": "^1.0.0", + "debug": "^4.3.6", + "pathe": "^1.1.2", "vite": "^5.0.0" }, "bin": { @@ -12228,31 +11849,30 @@ } }, "node_modules/vitest": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.6.0.tgz", - "integrity": "sha512-H5r/dN06swuFnzNFhq/dnz37bPXnq8xB2xB5JOVk8K09rUtoeNN+LHWkoQ0A/i3hvbUKKcCei9KpbxqHMLhLLA==", - "dev": true, - "dependencies": { - "@vitest/expect": "1.6.0", - "@vitest/runner": "1.6.0", - "@vitest/snapshot": "1.6.0", - "@vitest/spy": "1.6.0", - "@vitest/utils": "1.6.0", - "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.3", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.1.tgz", + "integrity": "sha512-97We7/VC0e9X5zBVkvt7SGQMGrRtn3KtySFQG5fpaMlS+l62eeXRQO633AYhSTC3z7IMebnPPNjGXVGNRFlxBA==", + "dev": true, + "dependencies": { + "@vitest/expect": "2.1.1", + "@vitest/mocker": "2.1.1", + "@vitest/pretty-format": "^2.1.1", + "@vitest/runner": "2.1.1", + "@vitest/snapshot": "2.1.1", + "@vitest/spy": "2.1.1", + "@vitest/utils": "2.1.1", + "chai": "^5.1.1", + "debug": "^4.3.6", + "magic-string": "^0.30.11", + "pathe": "^1.1.2", + "std-env": "^3.7.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.0", + "tinypool": "^1.0.0", + "tinyrainbow": "^1.2.0", "vite": "^5.0.0", - "vite-node": "1.6.0", - "why-is-node-running": "^2.2.2" + "vite-node": "2.1.1", + "why-is-node-running": "^2.3.0" }, "bin": { "vitest": "vitest.mjs" @@ -12266,164 +11886,30 @@ "peerDependencies": { "@edge-runtime/vm": "*", "@types/node": "^18.0.0 || >=20.0.0", - "@vitest/browser": "1.6.0", - "@vitest/ui": "1.6.0", + "@vitest/browser": "2.1.1", + "@vitest/ui": "2.1.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/vitest/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/vitest/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/vitest/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/vitest/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/vitest/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/vitest/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/vitest/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/vitest/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/vitest/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==", - "dev": true, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/vitest/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" + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } } }, "node_modules/walk-up-path": { @@ -12442,19 +11928,23 @@ } }, "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==", - "dev": true + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "engines": { + "node": ">=12" + } }, "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==", - "dev": true, + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-13.0.0.tgz", + "integrity": "sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==", "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" + "tr46": "^4.1.1", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=16" } }, "node_modules/which": { @@ -12471,41 +11961,6 @@ "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.15", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", - "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", - "dev": true, - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "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", @@ -12577,6 +12032,69 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "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==", + "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/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/wrap-ansi-cjs/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/wrap-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -12703,20 +12221,22 @@ "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, "engines": { "node": ">=10" } }, "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true }, "node_modules/yargs": { "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", @@ -12734,6 +12254,7 @@ "version": "21.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, "engines": { "node": ">=12" } @@ -12750,20 +12271,43 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/zod": { - "version": "3.23.8", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", - "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", - "funding": { - "url": "https://github.com/sponsors/colinhacks" + "packages/analysis": { + "name": "@jitar/analysis", + "version": "0.7.1", + "license": "MIT" + }, + "packages/build": { + "name": "@jitar/build", + "version": "0.7.4", + "license": "MIT", + "dependencies": { + "@jitar/analysis": "*", + "@jitar/configuration": "*", + "@jitar/execution": "*", + "@jitar/logging": "*", + "@jitar/sourcing": "*" } }, - "packages/caching": { - "version": "0.7.5", + "packages/cli": { + "name": "@jitar/cli", + "version": "0.7.4", + "license": "MIT", + "dependencies": { + "@jitar/build": "*", + "@jitar/configuration": "*", + "@jitar/http": "*", + "@jitar/runtime": "*", + "@jitar/sourcing": "*" + } + }, + "packages/configuration": { + "name": "@jitar/configuration", + "version": "0.7.4", "license": "MIT", "dependencies": { - "@jitar/reflection": "*", - "@jitar/runtime": "*" + "@jitar/sourcing": "*", + "@jitar/validation": "*", + "dotenv": "^16.4.5" } }, "packages/create-jitar": { @@ -12781,55 +12325,160 @@ "node": ">=20.0" } }, + "packages/errors": { + "name": "@jitar/errors", + "version": "0.7.4", + "license": "MIT" + }, + "packages/execution": { + "name": "@jitar/execution", + "version": "0.7.4", + "license": "MIT", + "dependencies": { + "@jitar/errors": "*" + } + }, + "packages/health": { + "name": "@jitar/health", + "version": "0.7.4", + "license": "MIT", + "dependencies": { + "@jitar/errors": "*" + } + }, + "packages/http": { + "name": "@jitar/http", + "version": "0.7.4", + "license": "MIT", + "dependencies": { + "@jitar/errors": "*", + "@jitar/execution": "*", + "@jitar/runtime": "*", + "@jitar/services": "*", + "@jitar/validation": "*" + } + }, "packages/jitar": { "version": "0.7.5", "license": "MIT", "dependencies": { + "dotenv": "^16.4.5", "express": "^4.19.2", - "express-http-proxy": "^2.0.0", "fs-extra": "^11.2.0", - "glob": "10.4.3", - "mime-types": "^2.1.35", - "tslog": "^4.9.3", - "yargs": "^17.7.2", - "zod": "^3.23.8" + "glob": "11.0.0", + "mime-types": "^2.1.35" + }, + "bin": { + "jitar": "dist/cli.js" }, "devDependencies": { - "@jitar/runtime": "*", - "@jitar/server-nodejs": "*" + "@jitar/cli": "*", + "@jitar/errors": "*", + "@jitar/execution": "*", + "@jitar/health": "*", + "@jitar/http": "*", + "@jitar/logging": "*", + "@jitar/middleware": "*" }, "engines": { "node": ">=20.0" } }, "packages/jitar/node_modules/glob": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.3.tgz", - "integrity": "sha512-Q38SGlYRpVtDBPSWEylRyctn7uDeTp4NQERTLiCT1FqA9JXPYWqAVmQU6qh4r/zMM5ehxTcbaO8EjhWnvEhmyg==", + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.0.tgz", + "integrity": "sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==", "dependencies": { "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", + "jackspeak": "^4.0.1", + "minimatch": "^10.0.0", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" + "path-scurry": "^2.0.0" }, "bin": { "glob": "dist/esm/bin.mjs" }, "engines": { - "node": ">=18" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, + "packages/jitar/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==", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/jitar/node_modules/lru-cache": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.1.tgz", + "integrity": "sha512-CgeuL5uom6j/ZVrg7G/+1IXqRY8JXX4Hghfy5YE0EhoYQWvndP1kufu58cmZLNIDKnRhZrXfdS9urVWx98AipQ==", + "engines": { + "node": "20 || >=22" + } + }, + "packages/jitar/node_modules/minimatch": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", + "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/jitar/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==", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/logging": { + "name": "@jitar/logging", + "version": "0.7.4", + "license": "MIT", + "dependencies": { + "@jitar/errors": "*", + "@jitar/execution": "*" + } + }, + "packages/middleware": { + "name": "@jitar/middleware", + "version": "0.7.4", + "license": "MIT", + "dependencies": { + "@jitar/errors": "*", + "@jitar/execution": "*" + } + }, "packages/plugin-vite": { + "name": "@jitar/plugin-vite", "version": "0.7.5", "license": "MIT", - "dependencies": { - "@jitar/reflection": "*" - }, "peerDependencies": { "vite": ">=4.0.0 || >=5.0.0" }, @@ -12839,66 +12488,133 @@ } } }, - "packages/reflection": { - "version": "0.7.5", - "license": "MIT" - }, "packages/runtime": { - "version": "0.7.5", + "name": "@jitar/runtime", + "version": "0.7.4", "license": "MIT", "dependencies": { - "@jitar/serialization": "*" + "@jitar/configuration": "*", + "@jitar/execution": "*", + "@jitar/health": "*", + "@jitar/middleware": "*", + "@jitar/serialization": "*", + "@jitar/services": "*", + "@jitar/sourcing": "*" } }, "packages/serialization": { - "version": "0.7.5", + "name": "@jitar/serialization", + "version": "0.7.4", "license": "MIT", "dependencies": { - "@jitar/reflection": "*" + "@jitar/analysis": "*" } }, - "packages/server-nodejs": { - "version": "0.7.5", + "packages/services": { + "name": "@jitar/services", + "version": "0.7.4", "license": "MIT", "dependencies": { - "@jitar/caching": "*", - "@jitar/runtime": "*", - "express": "^4.19.2", - "express-http-proxy": "^2.0.0", + "@jitar/errors": "*", + "@jitar/execution": "*", + "@jitar/sourcing": "*" + } + }, + "packages/sourcing": { + "name": "@jitar/sourcing", + "version": "0.7.4", + "license": "MIT", + "dependencies": { + "@jitar/errors": "*", "fs-extra": "^11.2.0", - "glob": "10.4.3", - "mime-types": "^2.1.35", - "tslog": "^4.9.3", - "yargs": "^17.7.2", - "zod": "^3.23.8" - }, - "engines": { - "node": ">=20.0" + "glob": "11.0.0", + "mime-types": "^2.1.35" } }, - "packages/server-nodejs/node_modules/glob": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.3.tgz", - "integrity": "sha512-Q38SGlYRpVtDBPSWEylRyctn7uDeTp4NQERTLiCT1FqA9JXPYWqAVmQU6qh4r/zMM5ehxTcbaO8EjhWnvEhmyg==", + "packages/sourcing/node_modules/glob": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.0.tgz", + "integrity": "sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==", "dependencies": { "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", + "jackspeak": "^4.0.1", + "minimatch": "^10.0.0", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" + "path-scurry": "^2.0.0" }, "bin": { "glob": "dist/esm/bin.mjs" }, "engines": { - "node": ">=18" + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/sourcing/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==", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/sourcing/node_modules/lru-cache": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.1.tgz", + "integrity": "sha512-CgeuL5uom6j/ZVrg7G/+1IXqRY8JXX4Hghfy5YE0EhoYQWvndP1kufu58cmZLNIDKnRhZrXfdS9urVWx98AipQ==", + "engines": { + "node": "20 || >=22" + } + }, + "packages/sourcing/node_modules/minimatch": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", + "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/sourcing/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==", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, + "packages/validation": { + "name": "@jitar/validation", + "version": "0.7.4", + "license": "MIT", + "dependencies": { + "@jitar/errors": "*", + "@jitar/execution": "*" + } + }, "tools/eslint-plugin": { + "name": "eslint-plugin-jitar", "version": "0.0.1" } } diff --git a/package.json b/package.json index 9d49d531..6b098cca 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,7 @@ "name": "jitar-monorepo", "version": "0.7.5", "private": true, + "description": "Monorepo configuration for Jitar.", "license": "MIT", "type": "module", "workspaces": [ @@ -21,26 +22,24 @@ "changelog-debug": "auto-changelog --template changelog.hbs -p --template json --output changelog-data.json" }, "devDependencies": { - "@rollup/plugin-node-resolve": "^15.2.3", - "@rollup/plugin-replace": "^5.0.7", + "@rollup/plugin-node-resolve": "^15.3.0", + "@rollup/plugin-replace": "^6.0.1", "@rollup/plugin-terser": "^0.4.4", - "@rollup/plugin-typescript": "^11.1.6", + "@rollup/plugin-typescript": "^12.1.0", "@types/express": "^4.17.21", - "@types/express-http-proxy": "^1.6.6", "@types/fs-extra": "^11.0.4", "@types/mime-types": "^2.1.4", "@types/prompts": "^2.4.9", - "@types/yargs": "^17.0.32", "@typescript-eslint/eslint-plugin": "^7.15.0", - "@vitest/coverage-v8": "^1.6.0", - "auto-changelog": "^2.4.0", + "@vitest/coverage-v8": "^2.1.1", + "auto-changelog": "^2.5.0", "degit": "^2.8.4", "eslint": "^8.57.0", "eslint-plugin-jitar": "0.0.1", "eslint-plugin-sonarjs": "^1.0.3", - "lerna": "^8.1.6", - "rollup": "^4.18.0", + "lerna": "^8.1.8", + "rollup": "^4.24.0", "rollup-plugin-dts": "^6.1.1", - "vitest": "^1.6.0" + "vitest": "^2.1.1" } } \ No newline at end of file diff --git a/packages/caching/CHANGELOG.md b/packages/analysis/CHANGELOG.md similarity index 100% rename from packages/caching/CHANGELOG.md rename to packages/analysis/CHANGELOG.md diff --git a/packages/analysis/README.md b/packages/analysis/README.md new file mode 100644 index 00000000..447c26a1 --- /dev/null +++ b/packages/analysis/README.md @@ -0,0 +1,72 @@ + +# Jitar Analysis + +This package provides application analysis tools for the [Jitar](https://jitar.dev) runtime. + +For more information about Jitar: + +* [Visit our website](https://jitar.dev) +* [Read the documentation](https://docs.jitar.dev). + +## Known limitations + +1. Declaration of multiple values is not supported + +```ts +// Supported +const a = 1; +export { a } + +// Unsupported (will be supported) +const b = 2, c = 3; +export { b, c } +``` + +2. Generator as object properties are not supported + +```ts +// Supported +function* myGenerator() { /* ... */ } + +// Supported +class Foo = +{ + *generator1() { /* ... */ } + + async *generator2() { /* ... */ } + + static *generator3() { /* ... */ } +}; + +// Unsupported (dynamic properties won't be supported) +class Bar +{ + *[Symbol.iterator]() { /* ... */ } +} +``` + +3. Destructuring not fully supported + +```ts +// Supported +const [ a, b = 42, ...others ] = myArray; +const { a, b = 42, ...others } = myObject; +``` + +```ts +// Aliases are not supported (will be supported) +const [ a, b: c, ...others ] = myArray; +const { a, b: c, ...others } = myObject; +``` + +```ts +// Nested destructuring is not supported (will be supported) +const [ a, [ b = 42, c, d ] ] = myArray; +const { a: { c, d = true }, b = 42 } = myObject; +``` + +```ts +// Dynamic property destructuring is not supported (won't be supported) +const [ [a]: b ] = myArray; +const { [a]: b } = myObject; +``` diff --git a/packages/analysis/package.json b/packages/analysis/package.json new file mode 100644 index 00000000..d55c6fa4 --- /dev/null +++ b/packages/analysis/package.json @@ -0,0 +1,18 @@ +{ + "name": "@jitar/analysis", + "version": "0.7.1", + "description": "Application analysis library for the Jitar runtime.", + "author": "Masking Technology (https://jitar.dev)", + "license": "MIT", + "type": "module", + "types": "dist/index.d.ts", + "exports": "./dist/index.js", + "private": true, + "scripts": { + "test": "vitest run", + "test-coverage": "vitest run --coverage", + "lint": "eslint . --ext .ts", + "build": "tsc -p tsconfig.json", + "clean": "rm -rf dist" + } +} diff --git a/packages/reflection/src/utils/ClassMerger.ts b/packages/analysis/src/dynamic/ClassMerger.ts similarity index 55% rename from packages/reflection/src/utils/ClassMerger.ts rename to packages/analysis/src/dynamic/ClassMerger.ts index 9c552348..2efc9e37 100644 --- a/packages/reflection/src/utils/ClassMerger.ts +++ b/packages/analysis/src/dynamic/ClassMerger.ts @@ -1,12 +1,9 @@ -import ReflectionClass from '../models/ReflectionClass.js'; -import ReflectionDeclaration from '../models/ReflectionDeclaration.js'; -import ReflectionFunction from '../models/ReflectionFunction.js'; -import ReflectionScope from '../models/ReflectionScope.js'; +import { ESClass, ESDeclaration, ESFunction, ESScope } from '../models'; export default class ClassMerger { - merge(model: ReflectionClass, parent: ReflectionClass): ReflectionClass + merge(model: ESClass, parent: ESClass): ESClass { const declarations = this.#mergeDeclarations(model.declarations, parent.declarations); const functions = this.#mergeFunctions(model.functions, parent.functions); @@ -15,12 +12,12 @@ export default class ClassMerger const members = [...declarations.values(), ...functions.values(), ...getters.values(), ...setters.values()]; - return new ReflectionClass(model.name, parent.name, new ReflectionScope(members)); + return new ESClass(model.name, parent.name, new ESScope(members)); } - #mergeDeclarations(model: ReflectionDeclaration[], parent: ReflectionDeclaration[]): ReflectionDeclaration[] + #mergeDeclarations(model: ESDeclaration[], parent: ESDeclaration[]): ESDeclaration[] { - const declarations = new Map(); + const declarations = new Map(); parent.forEach(declaration => declarations.set(declaration.name, declaration)); model.forEach(declaration => declarations.set(declaration.name, declaration)); @@ -28,9 +25,9 @@ export default class ClassMerger return [...declarations.values()]; } - #mergeFunctions(model: ReflectionFunction[], parent: ReflectionFunction[]): ReflectionFunction[] + #mergeFunctions(model: ESFunction[], parent: ESFunction[]): ESFunction[] { - const functions = new Map(); + const functions = new Map(); parent.forEach(funktion => functions.set(funktion.name, funktion)); model.forEach(funktion => functions.set(funktion.name, funktion)); diff --git a/packages/reflection/src/Reflector.ts b/packages/analysis/src/dynamic/Reflector.ts similarity index 54% rename from packages/reflection/src/Reflector.ts rename to packages/analysis/src/dynamic/Reflector.ts index 68e33fd2..aa4e9192 100644 --- a/packages/reflection/src/Reflector.ts +++ b/packages/analysis/src/dynamic/Reflector.ts @@ -1,63 +1,15 @@ -import Parser from './parser/Parser.js'; - -import ReflectionClass from './models/ReflectionClass.js'; -import ReflectionDeclaration from './models/ReflectionDeclaration.js'; -import ReflectionExpression from './models/ReflectionExpression.js'; -import ReflectionExport from './models/ReflectionExport.js'; -import ReflectionFunction from './models/ReflectionFunction.js'; -import ReflectionGetter from './models/ReflectionGetter.js'; -import ReflectionImport from './models/ReflectionImport.js'; -import ReflectionMember from './models/ReflectionMember.js'; -import ReflectionModule from './models/ReflectionModule.js'; -import ReflectionScope from './models/ReflectionScope.js'; -import ReflectionSetter from './models/ReflectionSetter.js'; -import ReflectionValue from './models/ReflectionValue.js'; - -import ClassMerger from './utils/ClassMerger.js'; +import { ESClass, ESDeclaration, ESExpression, ESFunction, ESGetter, ESMember, ESModule, ESScope, ESSetter, ESValue } from '../models'; +import { Parser } from '../static'; + +import ClassMerger from './ClassMerger'; export default class Reflector { - #parser: Parser; - #merger: ClassMerger; - - constructor(parser = new Parser(), merger = new ClassMerger()) - { - this.#parser = parser; - this.#merger = merger; - } - - parse(code: string): ReflectionModule - { - return this.#parser.parse(code); - } - - parseClass(code: string): ReflectionClass - { - return this.#parser.parseClass(code); - } - - parseFunction(code: string): ReflectionFunction - { - return this.#parser.parseFunction(code); - } + #parser = new Parser(); + #merger = new ClassMerger(); - parseDeclaration(code: string): ReflectionDeclaration - { - return this.#parser.parseDeclaration(code); - } - - parseImport(code: string): ReflectionImport - { - return this.#parser.parseImport(code); - } - - parseExport(code: string): ReflectionExport - { - return this.#parser.parseExport(code); - } - - fromModule(module: object, inherit = false): ReflectionModule + fromModule(module: object, inherit = false): ESModule { const entries = Object.entries(module); const members = []; @@ -81,16 +33,16 @@ export default class Reflector } else { - const expression = new ReflectionExpression(code); + const expression = new ESExpression(code); - members.push(new ReflectionDeclaration(key, expression)); + members.push(new ESDeclaration(key, expression)); } } - return new ReflectionModule(new ReflectionScope(members)); + return new ESModule(new ESScope(members)); } - fromClass(clazz: Function, inherit = false): ReflectionClass + fromClass(clazz: Function, inherit = false): ESClass { const model = this.isClass(clazz) ? this.#reflectStatic(clazz) @@ -113,18 +65,18 @@ export default class Reflector return this.#merger.merge(model, parentModel); } - fromObject(object: object, inherit = true): ReflectionClass + fromObject(object: object, inherit = true): ESClass { const clazz = this.getClass(object); return this.fromClass(clazz, inherit); } - fromFunction(funktion: Function): ReflectionFunction + fromFunction(funktion: Function): ESFunction { const code = funktion.toString(); - return this.parseFunction(code); + return this.#parser.parseFunction(code); } createInstance(clazz: Function, args: unknown[] = []): object @@ -164,23 +116,23 @@ export default class Reflector || clazz.toString().startsWith('async function'); } - #reflectStatic(clazz: Function): ReflectionClass + #reflectStatic(clazz: Function): ESClass { const code = clazz.toString(); - return this.parseClass(code); + return this.#parser.parseClass(code); } - #reflectDynamic(clazz: Function): ReflectionClass + #reflectDynamic(clazz: Function): ESClass { const object = this.createInstance(clazz); const members = this.#getMembers(clazz, object); - const scope = new ReflectionScope(members); + const scope = new ESScope(members); - return new ReflectionClass(clazz.name, undefined, scope); + return new ESClass(clazz.name, undefined, scope); } - #getMembers(clazz: Function, object: Object): ReflectionMember[] + #getMembers(clazz: Function, object: Object): ESMember[] { const declarations = this.#getDeclarations(object); const functions = this.#getFunctions(clazz); @@ -188,18 +140,18 @@ export default class Reflector return [...declarations, ...functions]; } - #getDeclarations(object: Object): ReflectionDeclaration[] + #getDeclarations(object: Object): ESDeclaration[] { const fieldNames = Object.getOwnPropertyNames(object); const values = object as Record; - const models: ReflectionDeclaration[] = []; + const models: ESDeclaration[] = []; for (const fieldName of fieldNames) { const content = values[fieldName]; - const value = content !== undefined ? new ReflectionValue(String(content)) : undefined; - const model = new ReflectionDeclaration(fieldName, value); + const value = content !== undefined ? new ESValue(String(content)) : undefined; + const model = new ESDeclaration(fieldName, value); models.push(model); } @@ -207,11 +159,11 @@ export default class Reflector return models; } - #getFunctions(clazz: Function): ReflectionFunction[] + #getFunctions(clazz: Function): ESFunction[] { const functionDescriptions = Object.getOwnPropertyDescriptors(clazz.prototype); - const models: ReflectionFunction[] = []; + const models: ESFunction[] = []; for (const functionName in functionDescriptions) { @@ -227,11 +179,11 @@ export default class Reflector if (description.get !== undefined) { - models.push(new ReflectionGetter(model.name, model.parameters, model.body, model.isStatic, model.isAsync, model.isPrivate)); + models.push(new ESGetter(model.name, model.parameters, model.body, model.isStatic, model.isAsync, model.isPrivate)); } else if (description.set !== undefined) { - models.push(new ReflectionSetter(model.name, model.parameters, model.body, model.isStatic, model.isAsync, model.isPrivate)); + models.push(new ESSetter(model.name, model.parameters, model.body, model.isStatic, model.isAsync, model.isPrivate)); } else { diff --git a/packages/analysis/src/dynamic/index.ts b/packages/analysis/src/dynamic/index.ts new file mode 100644 index 00000000..39ed58b3 --- /dev/null +++ b/packages/analysis/src/dynamic/index.ts @@ -0,0 +1,2 @@ + +export { default as Reflector } from './Reflector'; diff --git a/packages/analysis/src/index.ts b/packages/analysis/src/index.ts new file mode 100644 index 00000000..b3819fc7 --- /dev/null +++ b/packages/analysis/src/index.ts @@ -0,0 +1,9 @@ + +export { + ESAlias, ESArray, ESClass, ESDeclaration, ESDestructuredArray, ESDestructuredObject, ESDestructuredValue, + ESExport, ESExpression, ESField, ESFunction, ESGenerator, ESGetter, ESImport, ESMember, ESModule, + ESObject, ESParameter, ESSetter +} from './models'; + +export { Reflector } from './dynamic'; +export { Parser } from './static'; diff --git a/packages/reflection/src/models/ReflectionAlias.ts b/packages/analysis/src/models/ESAlias.ts similarity index 89% rename from packages/reflection/src/models/ReflectionAlias.ts rename to packages/analysis/src/models/ESAlias.ts index fea8495c..b2350602 100644 --- a/packages/reflection/src/models/ReflectionAlias.ts +++ b/packages/analysis/src/models/ESAlias.ts @@ -1,5 +1,5 @@ -export default class ReflectionAlias +export default class ESAlias { #name: string; #as: string; diff --git a/packages/analysis/src/models/ESArray.ts b/packages/analysis/src/models/ESArray.ts new file mode 100644 index 00000000..83b8345e --- /dev/null +++ b/packages/analysis/src/models/ESArray.ts @@ -0,0 +1,7 @@ + +import ESValue from './ESValue.js'; + +export default class ESArray extends ESValue +{ + +} diff --git a/packages/reflection/src/models/ReflectionClass.ts b/packages/analysis/src/models/ESClass.ts similarity index 59% rename from packages/reflection/src/models/ReflectionClass.ts rename to packages/analysis/src/models/ESClass.ts index b8148f40..9c6a35be 100644 --- a/packages/reflection/src/models/ReflectionClass.ts +++ b/packages/analysis/src/models/ESClass.ts @@ -1,18 +1,18 @@ -import ReflectionDeclaration from './ReflectionDeclaration.js'; -import ReflectionFunction from './ReflectionFunction.js'; -import ReflectionGenerator from './ReflectionGenerator.js'; -import ReflectionGetter from './ReflectionGetter.js'; -import ReflectionMember from './ReflectionMember.js'; -import ReflectionScope from './ReflectionScope.js'; -import ReflectionSetter from './ReflectionSetter.js'; - -export default class ReflectionClass extends ReflectionMember +import ESDeclaration from './ESDeclaration.js'; +import ESFunction from './ESFunction.js'; +import ESGenerator from './ESGenerator.js'; +import ESGetter from './ESGetter.js'; +import ESMember from './ESMember.js'; +import ESScope from './ESScope.js'; +import ESSetter from './ESSetter.js'; + +export default class ESClass extends ESMember { #parentName: string | undefined; - #scope: ReflectionScope; + #scope: ESScope; - constructor(name: string, parentName: string | undefined, scope: ReflectionScope) + constructor(name: string, parentName: string | undefined, scope: ESScope) { super(name); @@ -22,23 +22,23 @@ export default class ReflectionClass extends ReflectionMember get parentName(): string | undefined { return this.#parentName; } - get scope(): ReflectionScope { return this.#scope; } + get scope(): ESScope { return this.#scope; } - get members(): ReflectionMember[] { return this.#scope.members; } + get members(): ESMember[] { return this.#scope.members; } - get declarations(): ReflectionDeclaration[] { return this.#scope.declarations; } + get declarations(): ESDeclaration[] { return this.#scope.declarations; } - get functions(): ReflectionFunction[] { return this.#scope.functions; } + get functions(): ESFunction[] { return this.#scope.functions; } - get getters(): ReflectionGetter[] { return this.#scope.getters; } + get getters(): ESGetter[] { return this.#scope.getters; } - get setters(): ReflectionSetter[] { return this.#scope.setters; } + get setters(): ESSetter[] { return this.#scope.setters; } - get generators(): ReflectionGenerator[] { return this.#scope.generators; } + get generators(): ESGenerator[] { return this.#scope.generators; } - get readable(): Array + get readable(): Array { - const members = new Map(); + const members = new Map(); this.getters.forEach(getter => { members.set(getter.name, getter); }); this.declarations.forEach(declaration => { if(declaration.isPublic) members.set(declaration.name, declaration); }); @@ -46,9 +46,9 @@ export default class ReflectionClass extends ReflectionMember return [...members.values()]; } - get writable(): Array + get writable(): Array { - const members = new Map(); + const members = new Map(); this.setters.forEach(setter => { members.set(setter.name, setter); }); this.declarations.forEach(declaration => { if(declaration.isPublic) members.set(declaration.name, declaration); }); @@ -56,37 +56,37 @@ export default class ReflectionClass extends ReflectionMember return [...members.values()]; } - get callable(): ReflectionFunction[] + get callable(): ESFunction[] { return this.functions.filter(funktion => funktion.isPublic); } - getMember(name: string): ReflectionMember | undefined + getMember(name: string): ESMember | undefined { return this.#scope.getMember(name); } - getDeclaration(name: string): ReflectionDeclaration | undefined + getDeclaration(name: string): ESDeclaration | undefined { return this.#scope.getDeclaration(name); } - getFunction(name: string): ReflectionFunction | undefined + getFunction(name: string): ESFunction | undefined { return this.#scope.getFunction(name); } - getGetter(name: string): ReflectionGetter | undefined + getGetter(name: string): ESGetter | undefined { return this.#scope.getGetter(name); } - getSetter(name: string): ReflectionSetter | undefined + getSetter(name: string): ESSetter | undefined { return this.#scope.getSetter(name); } - getGenerator(name: string): ReflectionGenerator | undefined + getGenerator(name: string): ESGenerator | undefined { return this.#scope.getGenerator(name); } diff --git a/packages/analysis/src/models/ESDeclaration.ts b/packages/analysis/src/models/ESDeclaration.ts new file mode 100644 index 00000000..d2c1ab00 --- /dev/null +++ b/packages/analysis/src/models/ESDeclaration.ts @@ -0,0 +1,27 @@ + +import ESIdentifier from './ESIdentifier.js'; +import ESMember from './ESMember.js'; +import ESValue from './ESValue.js'; + +export default class ESDeclaration extends ESMember +{ + #identifier: ESIdentifier; + #value: ESValue | undefined; + + constructor(identifier: ESIdentifier, value: ESValue | undefined, isStatic = false, isPrivate = false) + { + super(identifier.toString(), isStatic, isPrivate); + + this.#identifier = identifier; + this.#value = value; + } + + get identifier() { return this.#identifier; } + + get value() { return this.#value; } + + toString(): string + { + return `${this.name}${this.value ? ' = ' + this.value.toString() : ''}`; + } +} diff --git a/packages/analysis/src/models/ESDestructuredArray.ts b/packages/analysis/src/models/ESDestructuredArray.ts new file mode 100644 index 00000000..439fc2e6 --- /dev/null +++ b/packages/analysis/src/models/ESDestructuredArray.ts @@ -0,0 +1,10 @@ + +import ESDestructuredValue from './ESDestructuredValue.js'; + +export default class ESDestructuredArray extends ESDestructuredValue +{ + toString(): string + { + return `[ ${super.toString()} ]`; + } +} diff --git a/packages/analysis/src/models/ESDestructuredObject.ts b/packages/analysis/src/models/ESDestructuredObject.ts new file mode 100644 index 00000000..0301119d --- /dev/null +++ b/packages/analysis/src/models/ESDestructuredObject.ts @@ -0,0 +1,10 @@ + +import ESDestructuredValue from './ESDestructuredValue.js'; + +export default class ESDestructuredObject extends ESDestructuredValue +{ + toString(): string + { + return `{ ${super.toString()} }`; + } +} diff --git a/packages/reflection/src/models/ReflectionDestructuredValue.ts b/packages/analysis/src/models/ESDestructuredValue.ts similarity index 51% rename from packages/reflection/src/models/ReflectionDestructuredValue.ts rename to packages/analysis/src/models/ESDestructuredValue.ts index cb620975..43174aae 100644 --- a/packages/reflection/src/models/ReflectionDestructuredValue.ts +++ b/packages/analysis/src/models/ESDestructuredValue.ts @@ -1,11 +1,11 @@ -import ReflectionParameter from './ReflectionParameter.js'; +import ESParameter from './ESParameter.js'; -export default class ReflectionDestructuredValue +export default class ESDestructuredValue { - #members: ReflectionParameter[]; + #members: ESParameter[]; - constructor(members: ReflectionParameter[]) + constructor(members: ESParameter[]) { this.#members = members; } diff --git a/packages/reflection/src/models/ReflectionExport.ts b/packages/analysis/src/models/ESExport.ts similarity index 59% rename from packages/reflection/src/models/ReflectionExport.ts rename to packages/analysis/src/models/ESExport.ts index 0d2eaabe..9365d4af 100644 --- a/packages/reflection/src/models/ReflectionExport.ts +++ b/packages/analysis/src/models/ESExport.ts @@ -1,13 +1,13 @@ -import ReflectionAlias from './ReflectionAlias.js'; -import ReflectionMember from './ReflectionMember.js'; +import ESAlias from './ESAlias.js'; +import ESMember from './ESMember.js'; -export default class ReflectionExport extends ReflectionMember +export default class ESExport extends ESMember { - #members: ReflectionAlias[]; + #members: ESAlias[]; #from: string | undefined; - constructor(members: ReflectionAlias[], from: string | undefined) + constructor(members: ESAlias[], from: string | undefined) { super(''); diff --git a/packages/analysis/src/models/ESExpression.ts b/packages/analysis/src/models/ESExpression.ts new file mode 100644 index 00000000..b19e39cd --- /dev/null +++ b/packages/analysis/src/models/ESExpression.ts @@ -0,0 +1,7 @@ + +import ESValue from './ESValue.js'; + +export default class ESExpression extends ESValue +{ + +} diff --git a/packages/reflection/src/models/ReflectionField.ts b/packages/analysis/src/models/ESField.ts similarity index 59% rename from packages/reflection/src/models/ReflectionField.ts rename to packages/analysis/src/models/ESField.ts index f27532a3..25a57413 100644 --- a/packages/reflection/src/models/ReflectionField.ts +++ b/packages/analysis/src/models/ESField.ts @@ -1,12 +1,12 @@ -import ReflectionValue from './ReflectionValue.js'; +import ESValue from './ESValue.js'; -export default class ReflectionField +export default class ESField { #name: string; - #value: ReflectionValue | undefined; + #value: ESValue | undefined; - constructor(name: string, value: ReflectionValue | undefined) + constructor(name: string, value: ESValue | undefined) { this.#name = name; this.#value = value; diff --git a/packages/reflection/src/models/ReflectionFunction.ts b/packages/analysis/src/models/ESFunction.ts similarity index 61% rename from packages/reflection/src/models/ReflectionFunction.ts rename to packages/analysis/src/models/ESFunction.ts index c56d8b97..d3a63562 100644 --- a/packages/reflection/src/models/ReflectionFunction.ts +++ b/packages/analysis/src/models/ESFunction.ts @@ -1,14 +1,14 @@ -import ReflectionMember from './ReflectionMember.js'; -import ReflectionParameter from './ReflectionParameter.js'; +import ESMember from './ESMember.js'; +import ESParameter from './ESParameter.js'; -export default class ReflectionFunction extends ReflectionMember +export default class ESFunction extends ESMember { - #parameters: ReflectionParameter[]; + #parameters: ESParameter[]; #body: string; #isAsync: boolean; - constructor(name: string, parameters: ReflectionParameter[], body: string, isStatic = false, isAsync = false, isPrivate = false) + constructor(name: string, parameters: ESParameter[], body: string, isStatic = false, isAsync = false, isPrivate = false) { super(name, isStatic, isPrivate); diff --git a/packages/reflection/src/models/ReflectionGenerator.ts b/packages/analysis/src/models/ESGenerator.ts similarity index 64% rename from packages/reflection/src/models/ReflectionGenerator.ts rename to packages/analysis/src/models/ESGenerator.ts index a5fe166e..eb7c6a6e 100644 --- a/packages/reflection/src/models/ReflectionGenerator.ts +++ b/packages/analysis/src/models/ESGenerator.ts @@ -1,7 +1,7 @@ -import ReflectionFunction from './ReflectionFunction.js'; +import ESFunction from './ESFunction.js'; -export default class ReflectionGenerator extends ReflectionFunction +export default class ESGenerator extends ESFunction { toString(): string { diff --git a/packages/analysis/src/models/ESGetter.ts b/packages/analysis/src/models/ESGetter.ts new file mode 100644 index 00000000..4bc4e68b --- /dev/null +++ b/packages/analysis/src/models/ESGetter.ts @@ -0,0 +1,10 @@ + +import ESFunction from './ESFunction.js'; + +export default class ESGetter extends ESFunction +{ + toString(): string + { + return `get ${super.toString()}`; + } +} diff --git a/packages/analysis/src/models/ESIdentifier.ts b/packages/analysis/src/models/ESIdentifier.ts new file mode 100644 index 00000000..67eaae59 --- /dev/null +++ b/packages/analysis/src/models/ESIdentifier.ts @@ -0,0 +1,7 @@ + +import ESDestructuredArray from './ESDestructuredArray.js'; +import ESDestructuredObject from './ESDestructuredObject.js'; + +type ESIdentifier = string | ESDestructuredArray | ESDestructuredObject + +export default ESIdentifier; diff --git a/packages/reflection/src/models/ReflectionImport.ts b/packages/analysis/src/models/ESImport.ts similarity index 57% rename from packages/reflection/src/models/ReflectionImport.ts rename to packages/analysis/src/models/ESImport.ts index 98d02dfc..97267170 100644 --- a/packages/reflection/src/models/ReflectionImport.ts +++ b/packages/analysis/src/models/ESImport.ts @@ -1,13 +1,13 @@ -import ReflectionAlias from './ReflectionAlias.js'; -import ReflectionMember from './ReflectionMember.js'; +import ESAlias from './ESAlias.js'; +import ESMember from './ESMember.js'; -export default class ReflectionImport extends ReflectionMember +export default class ESImport extends ESMember { - #members: ReflectionAlias[]; + #members: ESAlias[]; #from: string; - constructor(members: ReflectionAlias[], from: string) + constructor(members: ESAlias[], from: string) { super(''); diff --git a/packages/reflection/src/models/ReflectionMember.ts b/packages/analysis/src/models/ESMember.ts similarity index 92% rename from packages/reflection/src/models/ReflectionMember.ts rename to packages/analysis/src/models/ESMember.ts index 5fb210a5..3a564a1f 100644 --- a/packages/reflection/src/models/ReflectionMember.ts +++ b/packages/analysis/src/models/ESMember.ts @@ -1,5 +1,5 @@ -export default class ReflectionMember +export default class ESMember { #name: string; #isStatic: boolean; diff --git a/packages/analysis/src/models/ESModule.ts b/packages/analysis/src/models/ESModule.ts new file mode 100644 index 00000000..50653b9d --- /dev/null +++ b/packages/analysis/src/models/ESModule.ts @@ -0,0 +1,152 @@ + +import ESClass from './ESClass.js'; +import ESDeclaration from './ESDeclaration.js'; +import ESExport from './ESExport.js'; +import ESFunction from './ESFunction.js'; +import ESGenerator from './ESGenerator.js'; +import ESImport from './ESImport.js'; +import ESMember from './ESMember.js'; +import ESScope from './ESScope.js'; + +export default class ESModule +{ + #scope: ESScope; + + constructor(scope: ESScope) + { + this.#scope = scope; + } + + get scope(): ESScope { return this.#scope; } + + get members(): ESMember[] { return this.#scope.members; } + + get exportedMembers(): ESMember[] { return this.#filterExported(this.#scope.members); } + + get imports(): ESImport[] { return this.#scope.imports; } + + get exports(): ESExport[] { return this.#scope.exports; } + + get declarations(): ESDeclaration[] { return this.#scope.declarations; } + + get exportedDeclarations(): ESDeclaration[] { return this.#filterExported(this.#scope.declarations) as ESDeclaration[]; } + + get functions(): ESFunction[] { return this.#scope.functions; } + + get exportedFunctions(): ESFunction[] { return this.#filterExported(this.#scope.functions) as ESFunction[]; } + + get generators(): ESFunction[] { return this.#scope.generators; } + + get exportedGenerators(): ESGenerator[] { return this.#filterExported(this.#scope.generators) as ESGenerator[]; } + + get classes(): ESClass[] { return this.#scope.classes; } + + get exportedClasses(): ESClass[] { return this.#filterExported(this.#scope.classes) as ESClass[]; } + + get exported(): Map + { + const exported = new Map(); + + for (const exportItem of this.exports) + { + for (const alias of exportItem.members) + { + const member = this.getMember(alias.name); + + if (member !== undefined) + { + exported.set(alias.as, member); + } + } + } + + return exported; + } + + getMember(name: string): ESMember | undefined + { + return this.#scope.getMember(name); + } + + getDeclaration(name: string): ESDeclaration | undefined + { + return this.#scope.getDeclaration(name); + } + + getFunction(name: string): ESFunction | undefined + { + return this.#scope.getFunction(name); + } + + getGenerator(name: string): ESFunction | undefined + { + return this.#scope.getGenerator(name); + } + + getClass(name: string): ESClass | undefined + { + return this.#scope.getClass(name); + } + + hasMember(name: string): boolean + { + return this.#scope.hasMember(name); + } + + hasDeclaration(name: string): boolean + { + return this.#scope.hasDeclaration(name); + } + + hasFunction(name: string): boolean + { + return this.#scope.hasFunction(name); + } + + hasGenerator(name: string): boolean + { + return this.#scope.hasGenerator(name); + } + + hasClass(name: string): boolean + { + return this.#scope.hasClass(name); + } + + isExported(member: ESMember): boolean + { + for (const exportItem of this.exports) + { + for (const alias of exportItem.members) + { + if (alias.name === member.name) + { + return true; + } + } + } + + return false; + } + + getExported(name: string): ESMember | undefined + { + for (const exportItem of this.exports) + { + for (const alias of exportItem.members) + { + if (alias.as === name) + { + return this.getMember(alias.name); + } + } + } + + return undefined; + } + + #filterExported(members: ESMember[]): ESMember[] + { + return members.filter(member => this.isExported(member)); + } +} diff --git a/packages/analysis/src/models/ESObject.ts b/packages/analysis/src/models/ESObject.ts new file mode 100644 index 00000000..e83a2320 --- /dev/null +++ b/packages/analysis/src/models/ESObject.ts @@ -0,0 +1,7 @@ + +import ESValue from './ESValue.js'; + +export default class ESObject extends ESValue +{ + +} diff --git a/packages/analysis/src/models/ESParameter.ts b/packages/analysis/src/models/ESParameter.ts new file mode 100644 index 00000000..f51d62d8 --- /dev/null +++ b/packages/analysis/src/models/ESParameter.ts @@ -0,0 +1,8 @@ + +import ESDestructuredArray from './ESDestructuredArray.js'; +import ESField from './ESField.js'; +import ESDestructuredObject from './ESDestructuredObject.js'; + +type ESParameter = ESField | ESDestructuredObject | ESDestructuredArray; + +export default ESParameter; diff --git a/packages/analysis/src/models/ESScope.ts b/packages/analysis/src/models/ESScope.ts new file mode 100644 index 00000000..8709305f --- /dev/null +++ b/packages/analysis/src/models/ESScope.ts @@ -0,0 +1,126 @@ + +import ESMember from './ESMember.js'; +import ESGetter from './ESGetter.js'; +import ESSetter from './ESSetter.js'; +import ESFunction from './ESFunction.js'; +import ESClass from './ESClass.js'; +import ESImport from './ESImport.js'; +import ESExport from './ESExport.js'; +import ESGenerator from './ESGenerator.js'; +import ESDeclaration from './ESDeclaration.js'; + +// Required to work after minification. +const IMPORT_NAME = ESImport.name; +const EXPORT_NAME = ESExport.name; +const DECLARATION_NAME = ESDeclaration.name; +const FUNCTION_NAME = ESFunction.name; +const GETTER_NAME = ESGetter.name; +const SETTER_NAME = ESSetter.name; +const GENERATOR_NAME = ESGenerator.name; +const CLASS_NAME = ESClass.name; + +export default class ESScope +{ + #members: ESMember[]; + + constructor(members: ESMember[]) + { + this.#members = members; + } + + // The constructor name is used to determine the type of the member. + // This makes sure that the member is of the exact type and not a subclass. + + get members(): ESMember[] { return this.#members; } + + get imports(): ESImport[] { return this.#members.filter(member => member.constructor.name === IMPORT_NAME) as ESImport[]; } + + get exports(): ESExport[] { return this.#members.filter(member => member.constructor.name === EXPORT_NAME) as ESExport[]; } + + get declarations(): ESDeclaration[] { return this.#members.filter(member => member.constructor.name === DECLARATION_NAME) as ESDeclaration[]; } + + get functions(): ESFunction[] { return this.#members.filter(member => member.constructor.name === FUNCTION_NAME) as ESFunction[]; } + + get getters(): ESGetter[] { return this.#members.filter(member => member.constructor.name === GETTER_NAME) as ESGetter[]; } + + get setters(): ESSetter[] { return this.#members.filter(member => member.constructor.name === SETTER_NAME) as ESSetter[]; } + + get generators(): ESGenerator[] { return this.#members.filter(member => member.constructor.name === GENERATOR_NAME) as ESGenerator[]; } + + get classes(): ESClass[] { return this.#members.filter(member => member.constructor.name === CLASS_NAME) as ESClass[]; } + + getMember(name: string): ESMember | undefined + { + return this.#members.find(member => member.name === name); + } + + getDeclaration(name: string): ESDeclaration | undefined + { + return this.declarations.find(member => member.name === name); + } + + getFunction(name: string): ESFunction | undefined + { + return this.functions.find(member => member.name === name); + } + + getGetter(name: string): ESGetter | undefined + { + return this.getters.find(member => member.name === name); + } + + getSetter(name: string): ESSetter | undefined + { + return this.setters.find(member => member.name === name); + } + + getGenerator(name: string): ESGenerator | undefined + { + return this.generators.find(member => member.name === name); + } + + getClass(name: string): ESClass | undefined + { + return this.classes.find(member => member.name === name); + } + + hasMember(name: string): boolean + { + return this.getMember(name) !== undefined; + } + + hasDeclaration(name: string): boolean + { + return this.getDeclaration(name) !== undefined; + } + + hasFunction(name: string): boolean + { + return this.getFunction(name) !== undefined; + } + + hasGetter(name: string): boolean + { + return this.getGetter(name) !== undefined; + } + + hasSetter(name: string): boolean + { + return this.getSetter(name) !== undefined; + } + + hasGenerator(name: string): boolean + { + return this.getGenerator(name) !== undefined; + } + + hasClass(name: string): boolean + { + return this.getClass(name) !== undefined; + } + + toString(): string + { + return this.#members.map(member => member.toString()).join('\n'); + } +} diff --git a/packages/analysis/src/models/ESSetter.ts b/packages/analysis/src/models/ESSetter.ts new file mode 100644 index 00000000..4450e3ae --- /dev/null +++ b/packages/analysis/src/models/ESSetter.ts @@ -0,0 +1,10 @@ + +import ESFunction from './ESFunction.js'; + +export default class ESSetter extends ESFunction +{ + toString(): string + { + return `set ${super.toString()}`; + } +} diff --git a/packages/reflection/src/models/ReflectionValue.ts b/packages/analysis/src/models/ESValue.ts similarity index 86% rename from packages/reflection/src/models/ReflectionValue.ts rename to packages/analysis/src/models/ESValue.ts index 1c054086..b25bfc2e 100644 --- a/packages/reflection/src/models/ReflectionValue.ts +++ b/packages/analysis/src/models/ESValue.ts @@ -1,5 +1,5 @@ -export default class ReflectionValue +export default class ESValue { #definition: string; diff --git a/packages/analysis/src/models/index.ts b/packages/analysis/src/models/index.ts new file mode 100644 index 00000000..267b1b9e --- /dev/null +++ b/packages/analysis/src/models/index.ts @@ -0,0 +1,23 @@ + +export { default as ESAlias } from './ESAlias'; +export { default as ESArray } from './ESArray'; +export { default as ESClass } from './ESClass'; +export { default as ESDeclaration } from './ESDeclaration'; +export { default as ESDestructuredArray } from './ESDestructuredArray'; +export { default as ESDestructuredObject } from './ESDestructuredObject'; +export { default as ESDestructuredValue } from './ESDestructuredValue'; +export { default as ESExport } from './ESExport'; +export { default as ESExpression } from './ESExpression'; +export { default as ESField } from './ESField'; +export { default as ESFunction } from './ESFunction'; +export { default as ESGenerator } from './ESGenerator'; +export { default as ESGetter } from './ESGetter'; +export { default as ESIdentifier } from './ESIdentifier'; +export { default as ESImport } from './ESImport'; +export { default as ESMember } from './ESMember'; +export { default as ESModule } from './ESModule'; +export { default as ESObject } from './ESObject'; +export { default as ESParameter } from './ESParameter'; +export { default as ESScope } from './ESScope'; +export { default as ESSetter } from './ESSetter'; +export { default as ESValue } from './ESValue'; diff --git a/packages/reflection/src/parser/Lexer.ts b/packages/analysis/src/static/Lexer.ts similarity index 91% rename from packages/reflection/src/parser/Lexer.ts rename to packages/analysis/src/static/Lexer.ts index ca1757a7..5e03be44 100644 --- a/packages/reflection/src/parser/Lexer.ts +++ b/packages/analysis/src/static/Lexer.ts @@ -1,20 +1,20 @@ -import CharList from './CharList.js'; -import Token from './Token.js'; -import TokenList from './TokenList.js'; - -import { Comment, isComment } from './definitions/Comment.js'; -import { isDivider } from './definitions/Divider.js'; -import { isEmpty } from './definitions/Empty.js'; -import { Group, isGroup } from './definitions/Group.js'; -import { isKeyword } from './definitions/Keyword.js'; -import { List, isList } from './definitions/List.js'; -import { isLiteral } from './definitions/Literal.js'; -import { isOperator, Operator } from './definitions/Operator.js'; -import { Punctuation } from './definitions/Punctuation.js'; -import { isScope } from './definitions/Scope.js'; -import { TokenType } from './definitions/TokenType.js'; -import { Whitespace, isWhitespace } from './definitions/Whitespace.js'; +import CharList from './models/CharList'; +import Token from './models/Token'; +import TokenList from './models/TokenList'; + +import { Comment, isComment } from './definitions/Comment'; +import { isDivider } from './definitions/Divider'; +import { isEmpty } from './definitions/Empty'; +import { Group, isGroup } from './definitions/Group'; +import { isKeyword } from './definitions/Keyword'; +import { List, isList } from './definitions/List'; +import { isLiteral } from './definitions/Literal'; +import { isOperator, Operator } from './definitions/Operator'; +import { Punctuation } from './definitions/Punctuation'; +import { isScope } from './definitions/Scope'; +import { TokenType } from './definitions/TokenType'; +import { Whitespace, isWhitespace } from './definitions/Whitespace'; const ESCAPE_CHAR = '\\'; @@ -281,7 +281,7 @@ export default class Lexer #isIdentifier(char: string): boolean { // Values like numbers, booleans, null, etc. are parsed as literals. - // This is because they don't have any meaning in the reflection context. + // This is because they don't have any meaning in the analysis context. const isOther = isEmpty(char) || isWhitespace(char) diff --git a/packages/reflection/src/parser/Parser.ts b/packages/analysis/src/static/Parser.ts similarity index 75% rename from packages/reflection/src/parser/Parser.ts rename to packages/analysis/src/static/Parser.ts index 3f9a7ba1..372719be 100644 --- a/packages/reflection/src/parser/Parser.ts +++ b/packages/analysis/src/static/Parser.ts @@ -1,43 +1,28 @@ -import Lexer from './Lexer.js'; -import Token from './Token.js'; -import TokenList from './TokenList.js'; - -import { Divider, isDivider } from './definitions/Divider.js'; -import { Group } from './definitions/Group.js'; -import { Keyword, isDeclaration, isKeyword, isNotReserved } from './definitions/Keyword.js'; -import { List } from './definitions/List.js'; -import { Operator } from './definitions/Operator.js'; -import { Scope } from './definitions/Scope.js'; -import { TokenType } from './definitions/TokenType.js'; - -import ExpectedKeyword from './errors/ExpectedKeyword.js'; -import ExpectedToken from './errors/ExpectedToken.js'; -import UnexpectedKeyword from './errors/UnexpectedKeyword.js'; -import UnexpectedParseResult from './errors/UnexpectedParseResult.js'; -import UnexpectedToken from './errors/UnexpectedToken.js'; - -import ReflectionModule from '../models/ReflectionModule.js'; -import ReflectionMember from '../models/ReflectionMember.js'; -import ReflectionExport from '../models/ReflectionExport.js'; -import ReflectionClass from '../models/ReflectionClass.js'; -import ReflectionFunction from '../models/ReflectionFunction.js'; -import ReflectionField from '../models/ReflectionField.js'; -import ReflectionGetter from '../models/ReflectionGetter.js'; -import ReflectionSetter from '../models/ReflectionSetter.js'; -import ReflectionImport from '../models/ReflectionImport.js'; -import ReflectionGenerator from '../models/ReflectionGenerator.js'; -import ReflectionExpression from '../models/ReflectionExpression.js'; -import ReflectionArray from '../models/ReflectionArray.js'; -import ReflectionObject from '../models/ReflectionObject.js'; -import ReflectionAlias from '../models/ReflectionAlias.js'; -import ReflectionScope from '../models/ReflectionScope.js'; -import ReflectionValue from '../models/ReflectionValue.js'; -import ReflectionParameter from '../models/ReflectionParameter.js'; -import ReflectionDestructuredArray from '../models/ReflectionDestructuredArray.js'; -import ReflectionDestructuredObject from '../models/ReflectionDestructuredObject.js'; -import ReflectionDeclaration from '../models/ReflectionDeclaration.js'; -import ReflectionIdentifier from '../models/ReflectionIdentifier.js'; +import { + ESModule, ESMember, ESExport, ESClass, ESFunction, ESField, ESGetter, ESSetter, ESImport, + ESGenerator, ESExpression, ESArray, ESObject, ESAlias, ESScope, ESValue, ESParameter, + ESDestructuredArray, ESDestructuredObject, ESDeclaration, ESIdentifier +} from '../models'; + +import { Divider, isDivider } from './definitions/Divider'; +import { Group } from './definitions/Group'; +import { Keyword, isDeclaration, isKeyword, isNotReserved } from './definitions/Keyword'; +import { List } from './definitions/List'; +import { Operator } from './definitions/Operator'; +import { Scope } from './definitions/Scope'; +import { TokenType } from './definitions/TokenType'; + +import ExpectedKeyword from './errors/ExpectedKeyword'; +import ExpectedToken from './errors/ExpectedToken'; +import UnexpectedKeyword from './errors/UnexpectedKeyword'; +import UnexpectedParseResult from './errors/UnexpectedParseResult'; +import UnexpectedToken from './errors/UnexpectedToken'; + +import Token from './models/Token'; +import TokenList from './models/TokenList'; + +import Lexer from './Lexer'; const ANONYMOUS_IDENTIFIER = ''; const DEFAULT_IDENTIFIER = 'default'; @@ -53,117 +38,117 @@ export default class Parser this.#lexer = lexer; } - parse(code: string): ReflectionModule + parse(code: string): ESModule { const tokenList = this.#lexer.tokenize(code); const scope = this.#parseScope(tokenList); - return new ReflectionModule(scope); + return new ESModule(scope); } - parseFirst(code: string): ReflectionMember | ReflectionValue | undefined + parseFirst(code: string): ESMember | ESValue | undefined { const tokenList = this.#lexer.tokenize(code); return this.#parseNext(tokenList); } - parseValue(code: string): ReflectionValue + parseValue(code: string): ESValue { const model = this.parseFirst(code); - if ((model instanceof ReflectionValue) === false) + if ((model instanceof ESValue) === false) { throw new UnexpectedParseResult('a value definition'); } - return model as ReflectionValue; + return model as ESValue; } - parseImport(code: string): ReflectionImport + parseImport(code: string): ESImport { const model = this.parseFirst(code); - if ((model instanceof ReflectionImport) === false) + if ((model instanceof ESImport) === false) { throw new UnexpectedParseResult('an import definition'); } - return model; + return model as ESImport; } - parseExport(code: string): ReflectionExport + parseExport(code: string): ESExport { const model = this.parseFirst(code); - if ((model instanceof ReflectionExport) === false) + if ((model instanceof ESExport) === false) { throw new UnexpectedParseResult('an export definition'); } - return model; + return model as ESExport; } - parseDeclaration(code: string): ReflectionDeclaration + parseDeclaration(code: string): ESDeclaration { const model = this.parseFirst(code); - if ((model instanceof ReflectionDeclaration) === false) + if ((model instanceof ESDeclaration) === false) { throw new UnexpectedParseResult('a declaration definition'); } - return model; + return model as ESDeclaration; } - parseFunction(code: string): ReflectionFunction + parseFunction(code: string): ESFunction { const tokenList = this.#lexer.tokenize(code); const model = this.#parseMember(tokenList); - if ((model instanceof ReflectionFunction) === false) + if ((model instanceof ESFunction) === false) { throw new UnexpectedParseResult('a function definition'); } - return model; + return model as ESFunction; } - parseClass(code: string): ReflectionClass + parseClass(code: string): ESClass { const tokenList = this.#lexer.tokenize(code); const model = this.#parseMember(tokenList); - if ((model instanceof ReflectionClass) === false) + if ((model instanceof ESClass) === false) { throw new UnexpectedParseResult('a class definition'); } - return model; + return model as ESClass; } - #parseScope(tokenList: TokenList): ReflectionScope + #parseScope(tokenList: TokenList): ESScope { - const members: ReflectionMember[] = []; + const members: ESMember[] = []; while (tokenList.notAtEnd()) { const member = this.#parseNext(tokenList); - if (member instanceof ReflectionMember) + if (member instanceof ESMember) { - // Only reflection members are of interest + // Only ES members are of interest // because they can be exported members.push(member); } } - return new ReflectionScope(members); + return new ESScope(members); } // eslint-disable-next-line sonarjs/cognitive-complexity - #parseNext(tokenList: TokenList, isAsync = false): ReflectionMember | ReflectionValue | undefined + #parseNext(tokenList: TokenList, isAsync = false): ESMember | ESValue | undefined { const token = tokenList.current; @@ -251,7 +236,7 @@ export default class Parser throw new UnexpectedToken(token.value, token.start); } - #parseMember(tokenList: TokenList, isAsync = false): ReflectionMember + #parseMember(tokenList: TokenList, isAsync = false): ESMember { const token = tokenList.current; @@ -284,15 +269,15 @@ export default class Parser } } - #parseImport(tokenList: TokenList): ReflectionImport + #parseImport(tokenList: TokenList): ESImport { - const members: ReflectionAlias[] = []; + const members: ESAlias[] = []; let token = tokenList.current; if (token.isType(TokenType.LITERAL)) { - return new ReflectionImport(members, token.value); + return new ESImport(members, token.value); } else if (token.hasValue(Group.OPEN)) { @@ -302,7 +287,7 @@ export default class Parser tokenList.step(2); // Read away the from value and scope close - return new ReflectionImport(members, from); + return new ESImport(members, from); } if (token.hasValue(Scope.OPEN) === false) @@ -322,7 +307,7 @@ export default class Parser token = tokenList.step(); // Read away the alias name } - members.push(new ReflectionAlias(name, as)); + members.push(new ESAlias(name, as)); } if (token.hasValue(Divider.SEPARATOR)) @@ -349,10 +334,10 @@ export default class Parser tokenList.step(); // Read away the source - return new ReflectionImport(members, from); + return new ESImport(members, from); } - #parseExport(tokenList: TokenList): ReflectionExport + #parseExport(tokenList: TokenList): ESExport { const token = tokenList.current; @@ -370,7 +355,7 @@ export default class Parser } } - #parseSingleExport(tokenList: TokenList, isDefault: boolean): ReflectionExport + #parseSingleExport(tokenList: TokenList, isDefault: boolean): ESExport { let token = tokenList.current; let stepSize = 0; @@ -407,12 +392,12 @@ export default class Parser tokenList.stepBack(stepSize); // Step back to the original position } - const alias = new ReflectionAlias(name, as); + const alias = new ESAlias(name, as); - return new ReflectionExport([alias], from); + return new ESExport([alias], from); } - #parseMultiExport(tokenList: TokenList): ReflectionExport + #parseMultiExport(tokenList: TokenList): ESExport { const members = this.#parseAliasList(tokenList); @@ -427,10 +412,10 @@ export default class Parser tokenList.step(); // Read away the source - return new ReflectionExport(members, from); + return new ESExport(members, from); } - #parseAliasList(tokenList: TokenList): ReflectionAlias[] + #parseAliasList(tokenList: TokenList): ESAlias[] { const aliases = []; @@ -462,7 +447,7 @@ export default class Parser return aliases; } - #parseAlias(tokenList: TokenList): ReflectionAlias + #parseAlias(tokenList: TokenList): ESAlias { let token = tokenList.current; @@ -475,13 +460,13 @@ export default class Parser as = token.value; } - return new ReflectionAlias(name, as); + return new ESAlias(name, as); } - #parseDeclaration(tokenList: TokenList, isStatic: boolean, parseMultiple = false): ReflectionMember + #parseDeclaration(tokenList: TokenList, isStatic: boolean, parseMultiple = false): ESMember { let token = tokenList.current; - let identifier: ReflectionIdentifier = ANONYMOUS_IDENTIFIER; + let identifier: ESIdentifier = ANONYMOUS_IDENTIFIER; let isPrivate = false; if (token.hasValue(List.OPEN)) @@ -531,23 +516,23 @@ export default class Parser // Now we have the value we need to check if we need to recreate it // with the correct name, static and private properties. - if (value instanceof ReflectionGenerator) + if (value instanceof ESGenerator) { - return new ReflectionGenerator(identifier.toString(), value.parameters, value.body, isStatic, value.isAsync, isPrivate); + return new ESGenerator(identifier.toString(), value.parameters, value.body, isStatic, value.isAsync, isPrivate); } - else if (value instanceof ReflectionFunction) + else if (value instanceof ESFunction) { - return new ReflectionFunction(identifier.toString(), value.parameters, value.body, isStatic, value.isAsync, isPrivate); + return new ESFunction(identifier.toString(), value.parameters, value.body, isStatic, value.isAsync, isPrivate); } - else if (value instanceof ReflectionClass) + else if (value instanceof ESClass) { - return new ReflectionClass(identifier.toString(), value.parentName, value.scope); + return new ESClass(identifier.toString(), value.parentName, value.scope); } - return new ReflectionDeclaration(identifier, value as ReflectionValue, isStatic, isPrivate); + return new ESDeclaration(identifier, value as ESValue, isStatic, isPrivate); } - #parseFunction(tokenList: TokenList, isAsync: boolean, isStatic = false, isGetter = false, isSetter = false): ReflectionFunction + #parseFunction(tokenList: TokenList, isAsync: boolean, isStatic = false, isGetter = false, isSetter = false): ESFunction { let token = tokenList.current; let name = ANONYMOUS_IDENTIFIER; @@ -582,24 +567,24 @@ export default class Parser if (isGenerator) { - return new ReflectionGenerator(name, parameters, body, isStatic, isAsync, isPrivate); + return new ESGenerator(name, parameters, body, isStatic, isAsync, isPrivate); } else if (isGetter) { - return new ReflectionGetter(name, parameters, body, isStatic, isAsync, isPrivate); + return new ESGetter(name, parameters, body, isStatic, isAsync, isPrivate); } else if (isSetter) { - return new ReflectionSetter(name, parameters, body, isStatic, isAsync, isPrivate); + return new ESSetter(name, parameters, body, isStatic, isAsync, isPrivate); } - return new ReflectionFunction(name, parameters, body, isStatic, isAsync, isPrivate); + return new ESFunction(name, parameters, body, isStatic, isAsync, isPrivate); } - #parseArrowFunction(tokenList: TokenList, isAsync: boolean): ReflectionFunction + #parseArrowFunction(tokenList: TokenList, isAsync: boolean): ESFunction { let token = tokenList.current; - let parameters: ReflectionParameter[]; + let parameters: ESParameter[]; if (token.hasValue(Group.OPEN)) { @@ -608,7 +593,7 @@ export default class Parser } else { - parameters = [new ReflectionField(token.value, undefined)]; + parameters = [new ESField(token.value, undefined)]; token = tokenList.step(); } @@ -623,10 +608,10 @@ export default class Parser ? this.#parseBlock(tokenList, Scope.OPEN, Scope.CLOSE) : this.#parseExpression(tokenList).definition; - return new ReflectionFunction(ANONYMOUS_IDENTIFIER, parameters, body, false, isAsync, false); + return new ESFunction(ANONYMOUS_IDENTIFIER, parameters, body, false, isAsync, false); } - #parseParameters(tokenList: TokenList, closeId: string): ReflectionParameter[] + #parseParameters(tokenList: TokenList, closeId: string): ESParameter[] { const parameters = []; @@ -674,7 +659,7 @@ export default class Parser return parameters; } - #parseClass(tokenList: TokenList): ReflectionClass + #parseClass(tokenList: TokenList): ESClass { let token = tokenList.current; let name = ANONYMOUS_IDENTIFIER; @@ -702,10 +687,10 @@ export default class Parser const scope = this.#parseClassScope(tokenList); - return new ReflectionClass(name, parent, scope); + return new ESClass(name, parent, scope); } - #parseClassScope(tokenList: TokenList): ReflectionScope + #parseClassScope(tokenList: TokenList): ESScope { let token = tokenList.step(); // Read away the scope open @@ -727,10 +712,10 @@ export default class Parser token = tokenList.current; } - return new ReflectionScope(members); + return new ESScope(members); } - #parseClassMember(tokenList: TokenList): ReflectionMember + #parseClassMember(tokenList: TokenList): ESMember { let token = tokenList.current; @@ -777,35 +762,35 @@ export default class Parser : this.#parseDeclaration(tokenList, isStatic); } - #parseArray(tokenList: TokenList): ReflectionArray + #parseArray(tokenList: TokenList): ESArray { const items = this.#parseBlock(tokenList, List.OPEN, List.CLOSE); - return new ReflectionArray(items); + return new ESArray(items); } - #parseDestructuredArray(tokenList: TokenList): ReflectionDestructuredArray + #parseDestructuredArray(tokenList: TokenList): ESDestructuredArray { const fields = this.#parseParameters(tokenList, List.CLOSE); - return new ReflectionDestructuredArray(fields); + return new ESDestructuredArray(fields); } - #parseObject(tokenList: TokenList): ReflectionObject + #parseObject(tokenList: TokenList): ESObject { const fields = this.#parseBlock(tokenList, Scope.OPEN, Scope.CLOSE); - return new ReflectionObject(fields); + return new ESObject(fields); } - #parseDestructuredObject(tokenList: TokenList): ReflectionDestructuredObject + #parseDestructuredObject(tokenList: TokenList): ESDestructuredObject { const fields = this.#parseParameters(tokenList, Scope.CLOSE); - return new ReflectionDestructuredObject(fields); + return new ESDestructuredObject(fields); } - #parseField(tokenList: TokenList): ReflectionField + #parseField(tokenList: TokenList): ESField { let token = tokenList.current; @@ -819,13 +804,13 @@ export default class Parser { tokenList.step(); // Read away the assignment operator - value = this.#parseNext(tokenList, false) as ReflectionValue; + value = this.#parseNext(tokenList, false) as ESValue; } - return new ReflectionField(name, value); + return new ESField(name, value); } - #parseExpression(tokenList: TokenList): ReflectionExpression + #parseExpression(tokenList: TokenList): ESExpression { let token = tokenList.current; let code = ''; @@ -867,7 +852,7 @@ export default class Parser } } - return new ReflectionExpression(code.trim()); + return new ESExpression(code.trim()); } #parseBlock(tokenList: TokenList, openId: string, closeId: string): string diff --git a/packages/reflection/src/parser/definitions/Comment.ts b/packages/analysis/src/static/definitions/Comment.ts similarity index 100% rename from packages/reflection/src/parser/definitions/Comment.ts rename to packages/analysis/src/static/definitions/Comment.ts diff --git a/packages/reflection/src/parser/definitions/Divider.ts b/packages/analysis/src/static/definitions/Divider.ts similarity index 85% rename from packages/reflection/src/parser/definitions/Divider.ts rename to packages/analysis/src/static/definitions/Divider.ts index 51de291a..8b6404ea 100644 --- a/packages/reflection/src/parser/definitions/Divider.ts +++ b/packages/analysis/src/static/definitions/Divider.ts @@ -1,5 +1,5 @@ -import { Punctuation } from './Punctuation.js'; +import { Punctuation } from './Punctuation'; const Divider = { diff --git a/packages/reflection/src/parser/definitions/Empty.ts b/packages/analysis/src/static/definitions/Empty.ts similarity index 100% rename from packages/reflection/src/parser/definitions/Empty.ts rename to packages/analysis/src/static/definitions/Empty.ts diff --git a/packages/reflection/src/parser/definitions/Group.ts b/packages/analysis/src/static/definitions/Group.ts similarity index 82% rename from packages/reflection/src/parser/definitions/Group.ts rename to packages/analysis/src/static/definitions/Group.ts index 758ed03f..8c1f48f6 100644 --- a/packages/reflection/src/parser/definitions/Group.ts +++ b/packages/analysis/src/static/definitions/Group.ts @@ -1,5 +1,5 @@ -import { Punctuation } from './Punctuation.js'; +import { Punctuation } from './Punctuation'; const Group = { diff --git a/packages/reflection/src/parser/definitions/Keyword.ts b/packages/analysis/src/static/definitions/Keyword.ts similarity index 100% rename from packages/reflection/src/parser/definitions/Keyword.ts rename to packages/analysis/src/static/definitions/Keyword.ts diff --git a/packages/reflection/src/parser/definitions/List.ts b/packages/analysis/src/static/definitions/List.ts similarity index 82% rename from packages/reflection/src/parser/definitions/List.ts rename to packages/analysis/src/static/definitions/List.ts index 6a903f01..3ee0cb05 100644 --- a/packages/reflection/src/parser/definitions/List.ts +++ b/packages/analysis/src/static/definitions/List.ts @@ -1,5 +1,5 @@ -import { Punctuation } from './Punctuation.js'; +import { Punctuation } from './Punctuation'; const List = { diff --git a/packages/reflection/src/parser/definitions/Literal.ts b/packages/analysis/src/static/definitions/Literal.ts similarity index 100% rename from packages/reflection/src/parser/definitions/Literal.ts rename to packages/analysis/src/static/definitions/Literal.ts diff --git a/packages/reflection/src/parser/definitions/Operator.ts b/packages/analysis/src/static/definitions/Operator.ts similarity index 100% rename from packages/reflection/src/parser/definitions/Operator.ts rename to packages/analysis/src/static/definitions/Operator.ts diff --git a/packages/reflection/src/parser/definitions/Punctuation.ts b/packages/analysis/src/static/definitions/Punctuation.ts similarity index 100% rename from packages/reflection/src/parser/definitions/Punctuation.ts rename to packages/analysis/src/static/definitions/Punctuation.ts diff --git a/packages/reflection/src/parser/definitions/Scope.ts b/packages/analysis/src/static/definitions/Scope.ts similarity index 82% rename from packages/reflection/src/parser/definitions/Scope.ts rename to packages/analysis/src/static/definitions/Scope.ts index 3457a476..e79c5708 100644 --- a/packages/reflection/src/parser/definitions/Scope.ts +++ b/packages/analysis/src/static/definitions/Scope.ts @@ -1,5 +1,5 @@ -import { Punctuation } from './Punctuation.js'; +import { Punctuation } from './Punctuation'; const Scope = { diff --git a/packages/reflection/src/parser/definitions/TokenType.ts b/packages/analysis/src/static/definitions/TokenType.ts similarity index 100% rename from packages/reflection/src/parser/definitions/TokenType.ts rename to packages/analysis/src/static/definitions/TokenType.ts diff --git a/packages/reflection/src/parser/definitions/Whitespace.ts b/packages/analysis/src/static/definitions/Whitespace.ts similarity index 100% rename from packages/reflection/src/parser/definitions/Whitespace.ts rename to packages/analysis/src/static/definitions/Whitespace.ts diff --git a/packages/reflection/src/parser/errors/ExpectedKeyword.ts b/packages/analysis/src/static/errors/ExpectedKeyword.ts similarity index 100% rename from packages/reflection/src/parser/errors/ExpectedKeyword.ts rename to packages/analysis/src/static/errors/ExpectedKeyword.ts diff --git a/packages/reflection/src/parser/errors/ExpectedToken.ts b/packages/analysis/src/static/errors/ExpectedToken.ts similarity index 100% rename from packages/reflection/src/parser/errors/ExpectedToken.ts rename to packages/analysis/src/static/errors/ExpectedToken.ts diff --git a/packages/reflection/src/parser/errors/UnexpectedKeyword.ts b/packages/analysis/src/static/errors/UnexpectedKeyword.ts similarity index 100% rename from packages/reflection/src/parser/errors/UnexpectedKeyword.ts rename to packages/analysis/src/static/errors/UnexpectedKeyword.ts diff --git a/packages/reflection/src/parser/errors/UnexpectedParseResult.ts b/packages/analysis/src/static/errors/UnexpectedParseResult.ts similarity index 100% rename from packages/reflection/src/parser/errors/UnexpectedParseResult.ts rename to packages/analysis/src/static/errors/UnexpectedParseResult.ts diff --git a/packages/reflection/src/parser/errors/UnexpectedToken.ts b/packages/analysis/src/static/errors/UnexpectedToken.ts similarity index 100% rename from packages/reflection/src/parser/errors/UnexpectedToken.ts rename to packages/analysis/src/static/errors/UnexpectedToken.ts diff --git a/packages/analysis/src/static/index.ts b/packages/analysis/src/static/index.ts new file mode 100644 index 00000000..957dc6a1 --- /dev/null +++ b/packages/analysis/src/static/index.ts @@ -0,0 +1,16 @@ + +export * from './definitions/Comment'; +export * from './definitions/Divider'; +export * from './definitions/Empty'; +export * from './definitions/Group'; +export * from './definitions/Keyword'; +export * from './definitions/List'; +export * from './definitions/Literal'; +export * from './definitions/Operator'; +export * from './definitions/Punctuation'; +export * from './definitions/Scope'; +export * from './definitions/TokenType'; +export * from './definitions/Whitespace'; + +export { default as Lexer } from './Lexer'; +export { default as Parser } from './Parser'; diff --git a/packages/reflection/src/parser/CharList.ts b/packages/analysis/src/static/models/CharList.ts similarity index 77% rename from packages/reflection/src/parser/CharList.ts rename to packages/analysis/src/static/models/CharList.ts index c30a5c8a..abf3a00b 100644 --- a/packages/reflection/src/parser/CharList.ts +++ b/packages/analysis/src/static/models/CharList.ts @@ -1,5 +1,5 @@ -import ItemList from './ItemList.js'; +import ItemList from './ItemList'; export default class CharList extends ItemList { diff --git a/packages/reflection/src/parser/ItemList.ts b/packages/analysis/src/static/models/ItemList.ts similarity index 100% rename from packages/reflection/src/parser/ItemList.ts rename to packages/analysis/src/static/models/ItemList.ts diff --git a/packages/reflection/src/parser/Token.ts b/packages/analysis/src/static/models/Token.ts similarity index 100% rename from packages/reflection/src/parser/Token.ts rename to packages/analysis/src/static/models/Token.ts diff --git a/packages/analysis/src/static/models/TokenList.ts b/packages/analysis/src/static/models/TokenList.ts new file mode 100644 index 00000000..f117481e --- /dev/null +++ b/packages/analysis/src/static/models/TokenList.ts @@ -0,0 +1,8 @@ + +import ItemList from './ItemList'; +import Token from './Token'; + +export default class TokenList extends ItemList +{ + +} diff --git a/packages/reflection/test/Reflector.spec.ts b/packages/analysis/test/dynamic/Reflector.spec.ts similarity index 77% rename from packages/reflection/test/Reflector.spec.ts rename to packages/analysis/test/dynamic/Reflector.spec.ts index e3009232..8bead38a 100644 --- a/packages/reflection/test/Reflector.spec.ts +++ b/packages/analysis/test/dynamic/Reflector.spec.ts @@ -1,17 +1,10 @@ import { describe, expect, it } from 'vitest'; -import ReflectionExpression from '../src/models/ReflectionExpression'; -import ReflectionField from '../src/models/ReflectionField'; -import Reflector from '../src/Reflector'; +import { ESExpression, ESField } from '../../src/models'; +import { Reflector } from '../../src/dynamic'; -import -{ - Person, CustomError, - johnDoe, janeDoe, plainError, customError, - optionalFunction, - testModule -} from './_fixtures/Reflector.fixture'; +import { CLASSES, FUNCTIONS, OBJECTS, MODULES } from './fixtures'; const reflector = new Reflector(); @@ -21,7 +14,7 @@ describe('Reflector', () => { it('should get all members from a module', () => { - const reflectionModule = reflector.fromModule(testModule, false); + const reflectionModule = reflector.fromModule(MODULES.MIXED, false); const members = reflectionModule.members; expect(members.length).toBe(3); @@ -36,7 +29,7 @@ describe('Reflector', () => const classes = reflectionModule.classes; expect(classes.length).toBe(1); - expect(classes[0].name).toBe('Person'); + expect(classes[0].name).toBe('Child'); }); }); @@ -44,8 +37,8 @@ describe('Reflector', () => { it('should get class reflection model without parent members from a class', () => { - const reflectionClass = reflector.fromClass(Person, false); - expect(reflectionClass.name).toBe('Person'); + const reflectionClass = reflector.fromClass(CLASSES.Child, false); + expect(reflectionClass.name).toBe('Child'); const members = reflectionClass.members; expect(members.length).toBe(9); @@ -74,8 +67,8 @@ describe('Reflector', () => it('should get class reflection model with parent members from a class', () => { - const reflectionClass = reflector.fromClass(Person, true); - expect(reflectionClass.name).toBe('Person'); + const reflectionClass = reflector.fromClass(CLASSES.Child, true); + expect(reflectionClass.name).toBe('Child'); const members = reflectionClass.members; expect(members.length).toBe(11); @@ -124,7 +117,7 @@ describe('Reflector', () => it('should get class reflection model from a class with function based parent class', () => { - const reflectionClass = reflector.fromClass(CustomError, true); + const reflectionClass = reflector.fromClass(CLASSES.CustomError, true); expect(reflectionClass.name).toBe('CustomError'); const members = reflectionClass.members; @@ -151,8 +144,8 @@ describe('Reflector', () => { it('should get class reflection model without parent members from an object', () => { - const reflectionClass = reflector.fromObject(johnDoe, false); - expect(reflectionClass.name).toBe('Person'); + const reflectionClass = reflector.fromObject(OBJECTS.CLASS, false); + expect(reflectionClass.name).toBe('Child'); const members = reflectionClass.members; expect(members.length).toBe(9); @@ -160,8 +153,8 @@ describe('Reflector', () => it('should get class reflection model with parent members from an object', () => { - const reflectionClass = reflector.fromObject(johnDoe, true); - expect(reflectionClass.name).toBe('Person'); + const reflectionClass = reflector.fromObject(OBJECTS.CLASS, true); + expect(reflectionClass.name).toBe('Child'); const members = reflectionClass.members; expect(members.length).toBe(11); @@ -169,7 +162,7 @@ describe('Reflector', () => it('should get class reflection model from a function based class object', () => { - const reflectionClass = reflector.fromObject(plainError, true); + const reflectionClass = reflector.fromObject(OBJECTS.ERROR, true); expect(reflectionClass.name).toBe('Error'); const members = reflectionClass.members; @@ -178,7 +171,7 @@ describe('Reflector', () => it('should get class reflection model from a a class object with function based parent class', () => { - const reflectionClass = reflector.fromObject(customError, true); + const reflectionClass = reflector.fromObject(OBJECTS.CUSTOM_ERROR, true); expect(reflectionClass.name).toBe('CustomError'); const members = reflectionClass.members; @@ -190,25 +183,25 @@ describe('Reflector', () => { it('should get function reflection model', () => { - const functionFunction = reflector.fromFunction(optionalFunction); + const functionFunction = reflector.fromFunction(FUNCTIONS.OPTIONAL_ARGS); expect(functionFunction.name).toBe('optionalFunction'); expect(functionFunction.body).toBe('{ return a + b + c ; }'); const parameters = functionFunction.parameters; expect(parameters.length).toBe(3); - const first = parameters[0] as ReflectionField; + const first = parameters[0] as ESField; expect(first.name).toBe('a'); expect(first.value).toBe(undefined); - const second = parameters[1] as ReflectionField; + const second = parameters[1] as ESField; expect(second.name).toBe('b'); - expect(second.value).toBeInstanceOf(ReflectionExpression); - expect(second.value?.definition).toBe('new Person ( 1 , "Jane" , "Doe" , 42 )'); + expect(second.value).toBeInstanceOf(ESExpression); + expect(second.value?.definition).toBe('new Child ( 1 , "Jane" , "Doe" , 42 )'); - const third = parameters[2] as ReflectionField; + const third = parameters[2] as ESField; expect(third.name).toBe('c'); - expect(third.value).toBeInstanceOf(ReflectionExpression); + expect(third.value).toBeInstanceOf(ESExpression); expect(third.value?.definition).toBe('0'); }); }); @@ -217,8 +210,9 @@ describe('Reflector', () => { it('should get an instance class of a class', () => { - const peter = reflector.createInstance(Person, [1, 'Peter', 'van Vliet', 24]) as Person; - expect(peter).toBeInstanceOf(Person); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const peter = reflector.createInstance(CLASSES.Child, [1, 'Peter', 'van Vliet', 24]) as any; + expect(peter).toBeInstanceOf(CLASSES.Child); expect(peter.id).toBe(1); expect(peter.fullName).toBe('Peter van Vliet'); expect(peter.age).toBe(24); @@ -229,13 +223,13 @@ describe('Reflector', () => { it('should detect a class object', () => { - const result = reflector.isClassObject(johnDoe); + const result = reflector.isClassObject(OBJECTS.CLASS); expect(result).toBe(true); }); it('should detect a non class object', () => { - const result = reflector.isClassObject(janeDoe); + const result = reflector.isClassObject(OBJECTS.PLAIN); expect(result).toBe(false); }); }); @@ -244,9 +238,9 @@ describe('Reflector', () => { it('should get the class of an object', () => { - const result = reflector.getClass(johnDoe); + const result = reflector.getClass(OBJECTS.CLASS); - expect(result.name).toBe('Person'); + expect(result.name).toBe('Child'); }); }); @@ -254,9 +248,9 @@ describe('Reflector', () => { it('should get the parent of a class', () => { - const result = reflector.getParentClass(Person); + const result = reflector.getParentClass(CLASSES.Child); - expect(result.name).toBe('Human'); + expect(result.name).toBe('Parent'); }); }); }); diff --git a/packages/reflection/test/_fixtures/Reflector.fixture.ts b/packages/analysis/test/dynamic/fixtures/classes.fixture.ts similarity index 63% rename from packages/reflection/test/_fixtures/Reflector.fixture.ts rename to packages/analysis/test/dynamic/fixtures/classes.fixture.ts index 1d421330..bb292336 100644 --- a/packages/reflection/test/_fixtures/Reflector.fixture.ts +++ b/packages/analysis/test/dynamic/fixtures/classes.fixture.ts @@ -1,5 +1,5 @@ -class Human +class Parent { id: number; @@ -14,7 +14,7 @@ class Human } } -class Person extends Human +class Child extends Parent { #firstName: string; // No getter and setter #lastName: string; // No getter and setter @@ -58,27 +58,4 @@ class CustomError extends Error get additional() { return this.#additional; } } -const johnDoe = new Person(1, 'John', 'Doe', 42); -const janeDoe = { id: 2, fullName: 'Jane Doe', age: 42 }; -const plainError = new Error('Plain error'); -const customError = new CustomError('Custom error', 'with extras'); - -function requiredFunction(a: string, b: Person, c: number): string -{ - return a + b + c; -} - -function optionalFunction(a: string, b = new Person(1, 'Jane', 'Doe', 42), c = 0): string -{ - return a + b + c; -} - -const testModule = { Person, johnDoe, requiredFunction }; - -export -{ - Person, CustomError, - johnDoe, janeDoe, plainError, customError, - optionalFunction, - testModule -}; +export const CLASSES = { Parent, Child, CustomError }; diff --git a/packages/analysis/test/dynamic/fixtures/functions.fixture.ts b/packages/analysis/test/dynamic/fixtures/functions.fixture.ts new file mode 100644 index 00000000..58b84ac1 --- /dev/null +++ b/packages/analysis/test/dynamic/fixtures/functions.fixture.ts @@ -0,0 +1,20 @@ + +import { CLASSES } from "./classes.fixture"; + +const Child = CLASSES.Child; + +function requiredFunction(a: string, b: typeof Child, c: number): string +{ + return a + b + c; +} + +function optionalFunction(a: string, b = new Child(1, 'Jane', 'Doe', 42), c = 0): string +{ + return a + b + c; +} + +export const FUNCTIONS = +{ + REQUIRED_ARGS: requiredFunction, + OPTIONAL_ARGS: optionalFunction +}; diff --git a/packages/analysis/test/dynamic/fixtures/index.ts b/packages/analysis/test/dynamic/fixtures/index.ts new file mode 100644 index 00000000..4b294449 --- /dev/null +++ b/packages/analysis/test/dynamic/fixtures/index.ts @@ -0,0 +1,5 @@ + +export * from './classes.fixture'; +export * from './objects.fixture'; +export * from './functions.fixture'; +export * from './modules.fixture'; diff --git a/packages/analysis/test/dynamic/fixtures/modules.fixture.ts b/packages/analysis/test/dynamic/fixtures/modules.fixture.ts new file mode 100644 index 00000000..0717fd5b --- /dev/null +++ b/packages/analysis/test/dynamic/fixtures/modules.fixture.ts @@ -0,0 +1,16 @@ + +import { CLASSES } from "./classes.fixture"; +import { OBJECTS } from "./objects.fixture"; +import { FUNCTIONS } from "./functions.fixture"; + +const mixedModule = +{ + Person: CLASSES.Child, + johnDoe: OBJECTS.CLASS, + requiredFunction: FUNCTIONS.REQUIRED_ARGS +}; + +export const MODULES = +{ + MIXED: mixedModule +}; diff --git a/packages/analysis/test/dynamic/fixtures/objects.fixture.ts b/packages/analysis/test/dynamic/fixtures/objects.fixture.ts new file mode 100644 index 00000000..672817bb --- /dev/null +++ b/packages/analysis/test/dynamic/fixtures/objects.fixture.ts @@ -0,0 +1,10 @@ + +import { CLASSES } from "./classes.fixture"; + +export const OBJECTS = +{ + PLAIN: { id: 2, fullName: 'Jane Doe', age: 42 }, + CLASS: new CLASSES.Child(1, 'John', 'Doe', 42), + ERROR: new Error('Plain error'), + CUSTOM_ERROR: new CLASSES.CustomError('Custom error', 'with extras') +}; diff --git a/packages/reflection/test/models/ReflectionClass.spec.ts b/packages/analysis/test/models/ESClass.spec.ts similarity index 57% rename from packages/reflection/test/models/ReflectionClass.spec.ts rename to packages/analysis/test/models/ESClass.spec.ts index e2252808..7fd0244b 100644 --- a/packages/reflection/test/models/ReflectionClass.spec.ts +++ b/packages/analysis/test/models/ESClass.spec.ts @@ -1,14 +1,11 @@ import { describe, expect, it } from 'vitest'; -import ReflectionDeclaration from '../../src/models/ReflectionDeclaration'; -import ReflectionFunction from '../../src/models/ReflectionFunction'; -import ReflectionGetter from '../../src/models/ReflectionGetter'; -import ReflectionSetter from '../../src/models/ReflectionSetter'; +import { ESDeclaration, ESFunction, ESGetter, ESSetter } from '../../src/models'; -import { reflectionClass } from '../_fixtures/models/ReflectionClass.fixture'; +import { esClass } from './fixtures'; -describe('models/ReflectionClass', () => +describe('models/ESClass', () => { // Scope tests are omitted @@ -16,17 +13,17 @@ describe('models/ReflectionClass', () => { it('should return all readable members', () => { - const readable = reflectionClass.readable; + const readable = esClass.readable; expect(readable.length).toBe(3); expect(readable[0].name).toBe('name'); - expect(readable[0]).toBeInstanceOf(ReflectionGetter); + expect(readable[0]).toBeInstanceOf(ESGetter); expect(readable[1].name).toBe('age'); - expect(readable[1]).toBeInstanceOf(ReflectionGetter); + expect(readable[1]).toBeInstanceOf(ESGetter); expect(readable[2].name).toBe('length'); - expect(readable[2]).toBeInstanceOf(ReflectionDeclaration); + expect(readable[2]).toBeInstanceOf(ESDeclaration); }); }); @@ -34,14 +31,14 @@ describe('models/ReflectionClass', () => { it('should return all writable members', () => { - const writable = reflectionClass.writable; + const writable = esClass.writable; expect(writable.length).toBe(2); expect(writable[0].name).toBe('age'); - expect(writable[0]).toBeInstanceOf(ReflectionSetter); + expect(writable[0]).toBeInstanceOf(ESSetter); expect(writable[1].name).toBe('length'); - expect(writable[1]).toBeInstanceOf(ReflectionDeclaration); + expect(writable[1]).toBeInstanceOf(ESDeclaration); }); }); @@ -49,14 +46,14 @@ describe('models/ReflectionClass', () => { it('should return all callable members', () => { - const callable = reflectionClass.callable; + const callable = esClass.callable; expect(callable.length).toBe(2); expect(callable[0].name).toBe('constructor'); - expect(callable[0]).toBeInstanceOf(ReflectionFunction); + expect(callable[0]).toBeInstanceOf(ESFunction); expect(callable[1].name).toBe('toString'); - expect(callable[1]).toBeInstanceOf(ReflectionFunction); + expect(callable[1]).toBeInstanceOf(ESFunction); }); }); @@ -64,21 +61,21 @@ describe('models/ReflectionClass', () => { it('should read a public field', () => { - const canRead = reflectionClass.canRead('length'); + const canRead = esClass.canRead('length'); expect(canRead).toBe(true); }); it('should read a private field with getter', () => { - const canRead = reflectionClass.canRead('name'); + const canRead = esClass.canRead('name'); expect(canRead).toBe(true); }); it('should not read a private field without a getter', () => { - const canRead = reflectionClass.canRead('secret'); + const canRead = esClass.canRead('secret'); expect(canRead).toBe(false); }); @@ -88,21 +85,21 @@ describe('models/ReflectionClass', () => { it('should write a public field', () => { - const canWrite = reflectionClass.canWrite('length'); + const canWrite = esClass.canWrite('length'); expect(canWrite).toBe(true); }); it('should write a private field with setter', () => { - const canWrite = reflectionClass.canWrite('age'); + const canWrite = esClass.canWrite('age'); expect(canWrite).toBe(true); }); it('should not wite a private field without a setter', () => { - const canWrite = reflectionClass.canWrite('secret'); + const canWrite = esClass.canWrite('secret'); expect(canWrite).toBe(false); }); @@ -112,14 +109,14 @@ describe('models/ReflectionClass', () => { it('should call a public function', () => { - const canCall = reflectionClass.canCall('toString'); + const canCall = esClass.canCall('toString'); expect(canCall).toBe(true); }); it('should not call a private function', () => { - const canCall = reflectionClass.canCall('secretStuff'); + const canCall = esClass.canCall('secretStuff'); expect(canCall).toBe(false); }); diff --git a/packages/analysis/test/models/ESModule.spec.ts b/packages/analysis/test/models/ESModule.spec.ts new file mode 100644 index 00000000..7d82ba18 --- /dev/null +++ b/packages/analysis/test/models/ESModule.spec.ts @@ -0,0 +1,117 @@ + +import { describe, expect, it } from 'vitest'; + +import { ESClass, ESDeclaration, ESFunction, ESGenerator, ESMember } from '../../src/models'; + +import { esModule } from './fixtures'; + +describe('models/ESModule', () => +{ + // Scope tests are omitted + + describe('.exportedDeclarations', () => + { + it('should filter exported declarations', () => + { + const declarations = esModule.exportedDeclarations; + expect(declarations.length).toBe(1); + + expect(declarations[0]).toBeInstanceOf(ESDeclaration); + expect(declarations[0].name).toBe('peter'); + }); + }); + + describe('.exportedFunctions', () => + { + it('should filter exported functions', () => + { + const functions = esModule.exportedFunctions; + expect(functions.length).toBe(1); + + expect(functions[0]).toBeInstanceOf(ESFunction); + expect(functions[0].name).toBe('sum'); + }); + }); + + describe('.exportedGenerators', () => + { + it('should filter exported generators', () => + { + const generators = esModule.exportedGenerators; + expect(generators.length).toBe(1); + + expect(generators[0]).toBeInstanceOf(ESGenerator); + expect(generators[0].name).toBe('generateNumbers'); + }); + }); + + describe('.exportedClasses', () => + { + it('should filter exported classes', () => + { + const classes = esModule.exportedClasses; + expect(classes.length).toBe(1); + + expect(classes[0]).toBeInstanceOf(ESClass); + expect(classes[0].name).toBe('Customer'); + }); + }); + + describe('.exported', () => + { + it('should return a map with exported members only', () => + { + const exported = esModule.exported; + expect(exported.size).toBe(4); + + const first = exported.get('default') as ESFunction; + expect(first).toBeInstanceOf(ESFunction); + expect(first.name).toBe('sum'); + + const second = exported.get('peter') as ESDeclaration; + expect(second).toBeInstanceOf(ESDeclaration); + expect(second.name).toBe('peter'); + + const third = exported.get('Customer') as ESClass; + expect(third).toBeInstanceOf(ESClass); + expect(third.name).toBe('Customer'); + + const fourth = exported.get('generateNumbers') as ESGenerator; + expect(fourth).toBeInstanceOf(ESGenerator); + expect(fourth.name).toBe('generateNumbers'); + }); + }); + + describe('.isExported(member)', () => + { + it('should indicate that a member is exported', () => + { + const member = esModule.getMember('sum') as ESMember; + const result = esModule.isExported(member); + expect(result).toBe(true); + }); + + it('should indicate that a member is not exported', () => + { + const member = esModule.getMember('createJohn') as ESMember; + const result = esModule.isExported(member); + expect(result).toBe(false); + }); + }); + + describe('.getExported(name)', () => + { + it('should get an exported member', () => + { + const member = esModule.getExported('default') as ESFunction; + expect(member).toBeInstanceOf(ESFunction); + expect(member.name).toBe('sum'); + }); + + it('should not get a non-exported member', () => + { + const member = esModule.getExported('createJohn'); + expect(member).toBe(undefined); + }); + }); +}); diff --git a/packages/reflection/test/models/ReflectionScope.spec.ts b/packages/analysis/test/models/ESScope.spec.ts similarity index 58% rename from packages/reflection/test/models/ReflectionScope.spec.ts rename to packages/analysis/test/models/ESScope.spec.ts index f6597c37..72b4a745 100644 --- a/packages/reflection/test/models/ReflectionScope.spec.ts +++ b/packages/analysis/test/models/ESScope.spec.ts @@ -1,27 +1,20 @@ import { describe, expect, it } from 'vitest'; -import ReflectionClass from '../../src/models/ReflectionClass'; -import ReflectionDeclaration from '../../src/models/ReflectionDeclaration'; -import ReflectionExport from '../../src/models/ReflectionExport'; -import ReflectionFunction from '../../src/models/ReflectionFunction'; -import ReflectionGenerator from '../../src/models/ReflectionGenerator'; -import ReflectionGetter from '../../src/models/ReflectionGetter'; -import ReflectionImport from '../../src/models/ReflectionImport'; -import ReflectionSetter from '../../src/models/ReflectionSetter'; - -import { reflectionScope } from '../_fixtures/models/ReflectionScope.fixture'; - -describe('models/ReflectionScope', () => +import { ESImport, ESDeclaration, ESFunction, ESGenerator, ESClass, ESExport, ESGetter, ESSetter } from '../../src/models'; + +import { esScope } from './fixtures'; + +describe('models/ESScope', () => { describe('.imports', () => { it('should filter import members', () => { - const imports = reflectionScope.imports; + const imports = esScope.imports; expect(imports.length).toBe(1); - expect(imports[0]).toBeInstanceOf(ReflectionImport); + expect(imports[0]).toBeInstanceOf(ESImport); expect(imports[0].members.length).toBe(1); }); }); @@ -30,10 +23,10 @@ describe('models/ReflectionScope', () => { it('should filter export members', () => { - const exports = reflectionScope.exports; + const exports = esScope.exports; expect(exports.length).toBe(1); - expect(exports[0]).toBeInstanceOf(ReflectionExport); + expect(exports[0]).toBeInstanceOf(ESExport); expect(exports[0].members.length).toBe(2); }); }); @@ -42,13 +35,13 @@ describe('models/ReflectionScope', () => { it('should filter field members', () => { - const declarations = reflectionScope.declarations; + const declarations = esScope.declarations; expect(declarations.length).toBe(2); - expect(declarations[0]).toBeInstanceOf(ReflectionDeclaration); + expect(declarations[0]).toBeInstanceOf(ESDeclaration); expect(declarations[0].name).toBe('name'); - expect(declarations[1]).toBeInstanceOf(ReflectionDeclaration); + expect(declarations[1]).toBeInstanceOf(ESDeclaration); expect(declarations[1].name).toBe('age'); }); }); @@ -57,13 +50,13 @@ describe('models/ReflectionScope', () => { it('should filter function members', () => { - const functions = reflectionScope.functions; + const functions = esScope.functions; expect(functions.length).toBe(2); - expect(functions[0]).toBeInstanceOf(ReflectionFunction); + expect(functions[0]).toBeInstanceOf(ESFunction); expect(functions[0].name).toBe('createJohn'); - expect(functions[1]).toBeInstanceOf(ReflectionFunction); + expect(functions[1]).toBeInstanceOf(ESFunction); expect(functions[1].name).toBe('sum'); }); }); @@ -72,13 +65,13 @@ describe('models/ReflectionScope', () => { it('should filter getter members', () => { - const getters = reflectionScope.getters; + const getters = esScope.getters; expect(getters.length).toBe(2); - expect(getters[0]).toBeInstanceOf(ReflectionGetter); + expect(getters[0]).toBeInstanceOf(ESGetter); expect(getters[0].name).toBe('name'); - expect(getters[1]).toBeInstanceOf(ReflectionGetter); + expect(getters[1]).toBeInstanceOf(ESGetter); expect(getters[1].name).toBe('age'); }); }); @@ -87,10 +80,10 @@ describe('models/ReflectionScope', () => { it('should filter setter members', () => { - const setters = reflectionScope.setters; + const setters = esScope.setters; expect(setters.length).toBe(1); - expect(setters[0]).toBeInstanceOf(ReflectionSetter); + expect(setters[0]).toBeInstanceOf(ESSetter); expect(setters[0].name).toBe('age'); }); }); @@ -99,10 +92,10 @@ describe('models/ReflectionScope', () => { it('should filter generator members', () => { - const generators = reflectionScope.generators; + const generators = esScope.generators; expect(generators.length).toBe(1); - expect(generators[0]).toBeInstanceOf(ReflectionGenerator); + expect(generators[0]).toBeInstanceOf(ESGenerator); expect(generators[0].name).toBe('createJohn'); }); }); @@ -111,10 +104,10 @@ describe('models/ReflectionScope', () => { it('should filter class members', () => { - const classes = reflectionScope.classes; + const classes = esScope.classes; expect(classes.length).toBe(1); - expect(classes[0]).toBeInstanceOf(ReflectionClass); + expect(classes[0]).toBeInstanceOf(ESClass); expect(classes[0].name).toBe('Customer'); }); }); @@ -123,8 +116,8 @@ describe('models/ReflectionScope', () => { it('should get a member by its name', () => { - const member = reflectionScope.getMember('sum'); - expect(member).toBeInstanceOf(ReflectionFunction); + const member = esScope.getMember('sum'); + expect(member).toBeInstanceOf(ESFunction); expect(member?.name).toBe('sum'); }); }); @@ -133,8 +126,8 @@ describe('models/ReflectionScope', () => { it('should get a field by its name', () => { - const member = reflectionScope.getDeclaration('name'); - expect(member).toBeInstanceOf(ReflectionDeclaration); + const member = esScope.getDeclaration('name'); + expect(member).toBeInstanceOf(ESDeclaration); expect(member?.name).toBe('name'); }); }); @@ -143,8 +136,8 @@ describe('models/ReflectionScope', () => { it('should get a function by its name', () => { - const member = reflectionScope.getFunction('createJohn'); - expect(member).toBeInstanceOf(ReflectionFunction); + const member = esScope.getFunction('createJohn'); + expect(member).toBeInstanceOf(ESFunction); expect(member?.name).toBe('createJohn'); }); }); @@ -153,8 +146,8 @@ describe('models/ReflectionScope', () => { it('should get a getter by its name', () => { - const member = reflectionScope.getGetter('name'); - expect(member).toBeInstanceOf(ReflectionGetter); + const member = esScope.getGetter('name'); + expect(member).toBeInstanceOf(ESGetter); expect(member?.name).toBe('name'); }); }); @@ -163,8 +156,8 @@ describe('models/ReflectionScope', () => { it('should get a setter by its name', () => { - const member = reflectionScope.getSetter('age'); - expect(member).toBeInstanceOf(ReflectionSetter); + const member = esScope.getSetter('age'); + expect(member).toBeInstanceOf(ESSetter); expect(member?.name).toBe('age'); }); }); @@ -173,8 +166,8 @@ describe('models/ReflectionScope', () => { it('should get a generator by its name', () => { - const member = reflectionScope.getGenerator('createJohn'); - expect(member).toBeInstanceOf(ReflectionGenerator); + const member = esScope.getGenerator('createJohn'); + expect(member).toBeInstanceOf(ESGenerator); expect(member?.name).toBe('createJohn'); }); }); @@ -183,8 +176,8 @@ describe('models/ReflectionScope', () => { it('should get a class by its name', () => { - const member = reflectionScope.getClass('Customer'); - expect(member).toBeInstanceOf(ReflectionClass); + const member = esScope.getClass('Customer'); + expect(member).toBeInstanceOf(ESClass); expect(member?.name).toBe('Customer'); }); }); @@ -193,13 +186,13 @@ describe('models/ReflectionScope', () => { it('should have an existing member', () => { - const result = reflectionScope.hasMember('sum'); + const result = esScope.hasMember('sum'); expect(result).toBe(true); }); it('should not have a non-existing member', () => { - const result = reflectionScope.hasMember('nonExisting'); + const result = esScope.hasMember('nonExisting'); expect(result).toBe(false); }); }); @@ -208,13 +201,13 @@ describe('models/ReflectionScope', () => { it('should have an existing field', () => { - const result = reflectionScope.hasDeclaration('age'); + const result = esScope.hasDeclaration('age'); expect(result).toBe(true); }); it('should not have a non-existing field', () => { - const result = reflectionScope.hasDeclaration('Customer'); + const result = esScope.hasDeclaration('Customer'); expect(result).toBe(false); }); }); @@ -223,13 +216,13 @@ describe('models/ReflectionScope', () => { it('should have an existing function', () => { - const result = reflectionScope.hasFunction('createJohn'); + const result = esScope.hasFunction('createJohn'); expect(result).toBe(true); }); it('should not have a non-existing function', () => { - const result = reflectionScope.hasFunction('name'); + const result = esScope.hasFunction('name'); expect(result).toBe(false); }); }); @@ -238,13 +231,13 @@ describe('models/ReflectionScope', () => { it('should have an existing getter', () => { - const result = reflectionScope.hasGetter('name'); + const result = esScope.hasGetter('name'); expect(result).toBe(true); }); it('should not have a non-existing getter', () => { - const result = reflectionScope.hasGetter('createJohn'); + const result = esScope.hasGetter('createJohn'); expect(result).toBe(false); }); }); @@ -253,13 +246,13 @@ describe('models/ReflectionScope', () => { it('should have an existing setter', () => { - const result = reflectionScope.hasSetter('age'); + const result = esScope.hasSetter('age'); expect(result).toBe(true); }); it('should not have a non-existing setter', () => { - const result = reflectionScope.hasSetter('name'); + const result = esScope.hasSetter('name'); expect(result).toBe(false); }); }); @@ -268,13 +261,13 @@ describe('models/ReflectionScope', () => { it('should have an existing generator', () => { - const result = reflectionScope.hasGenerator('createJohn'); + const result = esScope.hasGenerator('createJohn'); expect(result).toBe(true); }); it('should not have a non-existing generator', () => { - const result = reflectionScope.hasGenerator('sum'); + const result = esScope.hasGenerator('sum'); expect(result).toBe(false); }); }); @@ -283,13 +276,13 @@ describe('models/ReflectionScope', () => { it('should have an existing class', () => { - const result = reflectionScope.hasClass('Customer'); + const result = esScope.hasClass('Customer'); expect(result).toBe(true); }); it('should not have a non-existing class', () => { - const result = reflectionScope.hasClass('name'); + const result = esScope.hasClass('name'); expect(result).toBe(false); }); }); diff --git a/packages/analysis/test/models/fixtures/esClass.fixture.ts b/packages/analysis/test/models/fixtures/esClass.fixture.ts new file mode 100644 index 00000000..cc3e7fdc --- /dev/null +++ b/packages/analysis/test/models/fixtures/esClass.fixture.ts @@ -0,0 +1,20 @@ + +import { ESClass, ESDeclaration, ESExpression, ESField, ESFunction, ESGetter, ESScope, ESSetter } from '../../../src/models'; + +const members = +[ + new ESDeclaration('name', new ESExpression('"Peter"'), false, true), + new ESDeclaration('age', undefined, false, true), + new ESDeclaration('length', undefined, false, false), + new ESDeclaration('secret', undefined, false, true), + new ESFunction('constructor', [new ESField('age', undefined)], 'this.#age = age;'), + new ESGetter('name', [], 'return this.#name;'), + new ESGetter('age', [], 'return this.#age;'), + new ESSetter('age', [new ESField('age', undefined)], 'this.#age = age;'), + new ESFunction('secretStuff', [], '', false, false, true), + new ESFunction('toString', [], 'return `${this.#name} (${this.#age})`'), +]; + +const scope = new ESScope(members); + +export const esClass = new ESClass('Person', undefined, scope); diff --git a/packages/analysis/test/models/fixtures/esModule.fixture.ts b/packages/analysis/test/models/fixtures/esModule.fixture.ts new file mode 100644 index 00000000..e0376cfa --- /dev/null +++ b/packages/analysis/test/models/fixtures/esModule.fixture.ts @@ -0,0 +1,25 @@ + +import { ESImport, ESAlias, ESDeclaration, ESExpression, ESFunction, ESField, ESGenerator, ESClass, ESScope, ESExport, ESModule } from '../../../src/models'; + +const members = +[ + new ESImport([new ESAlias('default', 'Person')], './Person.js'), + new ESDeclaration('peter', new ESExpression('new Person("Peter")')), + new ESDeclaration('bas', new ESExpression('new Person("Bas")')), + new ESFunction('createJohn', [], 'return new Person("John")'), + new ESFunction('sum', [new ESField('a', undefined), new ESField('b', undefined)], 'return a + b'), + new ESGenerator('generateNumbers', [new ESField('count', undefined)], 'for (let i = 0; i < count; i++) yield i;'), + new ESGenerator('createJohn', [], 'yield new Person("John")'), + new ESClass('Customer', 'Person', new ESScope([])), + new ESClass('Order', undefined, new ESScope([])), + new ESExport([ + new ESAlias('sum', 'default'), + new ESAlias('peter', 'peter'), + new ESAlias('Customer', 'Customer'), + new ESAlias('generateNumbers', 'generateNumbers') + ], undefined) +]; + +const scope = new ESScope(members); + +export const esModule = new ESModule(scope); diff --git a/packages/analysis/test/models/fixtures/esScope.fixture.ts b/packages/analysis/test/models/fixtures/esScope.fixture.ts new file mode 100644 index 00000000..ecb9bdec --- /dev/null +++ b/packages/analysis/test/models/fixtures/esScope.fixture.ts @@ -0,0 +1,19 @@ + +import { ESImport, ESAlias, ESDeclaration, ESExpression, ESFunction, ESField, ESGenerator, ESClass, ESScope, ESExport, ESGetter, ESSetter } from '../../../src/models'; + +const members = +[ + new ESImport([new ESAlias('default', 'Person')], './Person.js'), + new ESDeclaration('name', new ESExpression('"john"'), false, true), + new ESDeclaration('age', new ESExpression('42'), false, true), + new ESFunction('createJohn', [], '{ return new Person("John") }'), + new ESFunction('sum', [new ESField('a', undefined), new ESField('b', undefined)], 'return a + b'), + new ESGetter('name', [], 'return this.#name;'), + new ESGetter('age', [], 'return this.#age;'), + new ESSetter('age', [new ESField('age', undefined)], 'this.#age = age;'), + new ESClass('Customer', 'Person', new ESScope([])), + new ESGenerator('createJohn', [], 'yield new Person("John")'), + new ESExport([new ESAlias('Customer', 'Customer'), new ESAlias('sum', 'default')], undefined) +]; + +export const esScope = new ESScope(members); diff --git a/packages/analysis/test/models/fixtures/index.ts b/packages/analysis/test/models/fixtures/index.ts new file mode 100644 index 00000000..c87b52d8 --- /dev/null +++ b/packages/analysis/test/models/fixtures/index.ts @@ -0,0 +1,4 @@ + +export * from './esClass.fixture'; +export * from './esModule.fixture'; +export * from './esScope.fixture'; diff --git a/packages/reflection/test/parser/Lexer.spec.ts b/packages/analysis/test/static/Lexer.spec.ts similarity index 94% rename from packages/reflection/test/parser/Lexer.spec.ts rename to packages/analysis/test/static/Lexer.spec.ts index 54edd753..35e0df71 100644 --- a/packages/reflection/test/parser/Lexer.spec.ts +++ b/packages/analysis/test/static/Lexer.spec.ts @@ -1,16 +1,9 @@ import { describe, expect, it } from 'vitest'; -import { Divider } from '../../src/parser/definitions/Divider'; -import { Group } from '../../src/parser/definitions/Group'; -import { Keyword } from '../../src/parser/definitions/Keyword'; -import { List } from '../../src/parser/definitions/List'; -import { Operator } from '../../src/parser/definitions/Operator'; -import { Scope } from '../../src/parser/definitions/Scope'; -import { TokenType } from '../../src/parser/definitions/TokenType'; -import Lexer from '../../src/parser/Lexer'; - -import { CODE } from '../_fixtures/parser/Lexer.fixture'; +import { Divider, Group, Keyword, List, Operator, Scope, TokenType, Lexer } from '../../src/static'; + +import { CODE } from './fixtures'; const lexer = new Lexer(); diff --git a/packages/reflection/test/parser/Parser.spec.ts b/packages/analysis/test/static/Parser.spec.ts similarity index 82% rename from packages/reflection/test/parser/Parser.spec.ts rename to packages/analysis/test/static/Parser.spec.ts index 45c3d2c3..24330a08 100644 --- a/packages/reflection/test/parser/Parser.spec.ts +++ b/packages/analysis/test/static/Parser.spec.ts @@ -1,16 +1,10 @@ import { describe, expect, it } from 'vitest'; -import ReflectionArray from '../../src/models/ReflectionArray'; -import ReflectionDestructuredArray from '../../src/models/ReflectionDestructuredArray'; -import ReflectionDestructuredObject from '../../src/models/ReflectionDestructuredObject'; -import ReflectionExpression from '../../src/models/ReflectionExpression'; -import ReflectionField from '../../src/models/ReflectionField'; -import ReflectionGenerator from '../../src/models/ReflectionGenerator'; -import ReflectionObject from '../../src/models/ReflectionObject'; -import Parser from '../../src/parser/Parser'; +import { ESArray, ESObject, ESExpression, ESField, ESGenerator, ESDestructuredArray, ESDestructuredObject } from '../../src/models'; +import { Parser } from '../../src/static'; -import { VALUES, IMPORTS, EXPORTS, DECLARATIONS, FUNCTIONS, CLASSES, MODULES } from '../_fixtures/parser/Parser.fixture'; +import { VALUES, IMPORTS, EXPORTS, DECLARATIONS, FUNCTIONS, CLASSES, MODULES } from './fixtures'; const parser = new Parser(); @@ -21,42 +15,42 @@ describe('parser/Parser', () => it('should parse an array', () => { const value = parser.parseValue(VALUES.ARRAY); - expect(value).toBeInstanceOf(ReflectionArray); + expect(value).toBeInstanceOf(ESArray); expect(value.definition).toBe('[ 1 , "foo" , false , new Person ( "Peter" , 42 ) , { a : 1 , b : 2 } ]'); }); it('should parse an object', () => { const value = parser.parseValue(VALUES.OBJECT); - expect(value).toBeInstanceOf(ReflectionObject); + expect(value).toBeInstanceOf(ESObject); expect(value.definition).toBe('{ key1 : "value1" , "key2" : new Person ( ) .toString ( ) }'); }); it('should parse an expression', () => { const value = parser.parseValue(VALUES.EXPRESSION); - expect(value).toBeInstanceOf(ReflectionExpression); + expect(value).toBeInstanceOf(ESExpression); expect(value.definition).toBe('new Number ( Math.ceil ( Math.random ( ) ) + 10 ) .toString ( )'); }); it('should parse a grouped expression', () => { const value = parser.parseValue(VALUES.EXPRESSION_GROUP); - expect(value).toBeInstanceOf(ReflectionExpression); + expect(value).toBeInstanceOf(ESExpression); expect(value.definition).toBe('( a + b ) * c'); }); it('should parse an if...else expression', () => { const value = parser.parseValue(VALUES.IF_ELSE); - expect(value).toBeInstanceOf(ReflectionExpression); + expect(value).toBeInstanceOf(ESExpression); expect(value.definition).toBe('if ( true ) { return "value1" ; } else { return "value2" ; }'); }); it('should parse an try...catch...finally expression', () => { const value = parser.parseValue(VALUES.TRY_CATCH_FINALLY); - expect(value).toBeInstanceOf(ReflectionExpression); + expect(value).toBeInstanceOf(ESExpression); expect(value.definition).toBe('try { sum ( 1 , 2 ) ; } catch ( error ) { console.error ( error ) ; } finally { console.log ( "finally" ) ; }'); }); }); @@ -303,7 +297,7 @@ describe('parser/Parser', () => const declaration = parser.parseDeclaration(DECLARATIONS.CONST); expect(declaration.name).toBe('name'); - expect(declaration.value).toBeInstanceOf(ReflectionExpression); + expect(declaration.value).toBeInstanceOf(ESExpression); expect(declaration.value?.definition).toBe("'const'"); }); @@ -312,7 +306,7 @@ describe('parser/Parser', () => const declaration = parser.parseDeclaration(DECLARATIONS.LET); expect(declaration.name).toBe('name'); - expect(declaration.value).toBeInstanceOf(ReflectionExpression); + expect(declaration.value).toBeInstanceOf(ESExpression); expect(declaration.value?.definition).toBe("'let'"); }); @@ -321,7 +315,7 @@ describe('parser/Parser', () => const declaration = parser.parseDeclaration(DECLARATIONS.VAR); expect(declaration.name).toBe('name'); - expect(declaration.value).toBeInstanceOf(ReflectionExpression); + expect(declaration.value).toBeInstanceOf(ESExpression); expect(declaration.value?.definition).toBe("'var'"); }); @@ -330,7 +324,7 @@ describe('parser/Parser', () => const declaration = parser.parseDeclaration(DECLARATIONS.MULTIPLE); expect(declaration.name).toBe('name1'); - expect(declaration.value).toBeInstanceOf(ReflectionExpression); + expect(declaration.value).toBeInstanceOf(ESExpression); expect(declaration.value?.definition).toBe('( 1 + 2 ) * 3'); }); @@ -339,7 +333,7 @@ describe('parser/Parser', () => const declaration = parser.parseDeclaration(DECLARATIONS.EXPRESSION); expect(declaration.name).toBe('number'); - expect(declaration.value).toBeInstanceOf(ReflectionExpression); + expect(declaration.value).toBeInstanceOf(ESExpression); expect(declaration.value?.definition).toBe("new Number ( Math.ceil ( Math.random ( ) ) + 10 ) .toString ( )"); }); @@ -348,7 +342,7 @@ describe('parser/Parser', () => const declaration = parser.parseDeclaration(DECLARATIONS.ARRAY); expect(declaration.name).toBe('array'); - expect(declaration.value).toBeInstanceOf(ReflectionArray); + expect(declaration.value).toBeInstanceOf(ESArray); expect(declaration.value?.definition).toBe("[ 'value1' , 'value2' ]"); }); @@ -357,7 +351,7 @@ describe('parser/Parser', () => const declaration = parser.parseDeclaration(DECLARATIONS.OBJECT); expect(declaration.name).toBe('object'); - expect(declaration.value).toBeInstanceOf(ReflectionObject); + expect(declaration.value).toBeInstanceOf(ESObject); expect(declaration.value?.definition).toBe("{ key1 : 'value1' , key2 : 'value2' }"); }); @@ -366,7 +360,7 @@ describe('parser/Parser', () => const declaration = parser.parseDeclaration(DECLARATIONS.REGEX); expect(declaration.name).toBe('regex'); - expect(declaration.value).toBeInstanceOf(ReflectionExpression); + expect(declaration.value).toBeInstanceOf(ESExpression); expect(declaration.value?.definition).toBe("/regex/g"); }); @@ -375,21 +369,21 @@ describe('parser/Parser', () => const declaration = parser.parseDeclaration(DECLARATIONS.DESTRUCTURING_ARRAY); expect(declaration.name).toBe('[ value1 , value2 = true ]'); - expect(declaration.value).toBeInstanceOf(ReflectionExpression); - expect(declaration.identifier).toBeInstanceOf(ReflectionDestructuredArray); + expect(declaration.value).toBeInstanceOf(ESExpression); + expect(declaration.identifier).toBeInstanceOf(ESDestructuredArray); - const identifier = declaration.identifier as ReflectionDestructuredArray; + const identifier = declaration.identifier as ESDestructuredArray; expect(identifier.members.length).toBe(2); - const firstMember = identifier.members[0] as ReflectionField; - expect(firstMember).toBeInstanceOf(ReflectionField); + const firstMember = identifier.members[0] as ESField; + expect(firstMember).toBeInstanceOf(ESField); expect(firstMember.name).toBe('value1'); expect(firstMember.value).toBe(undefined); - const secondMember = identifier.members[1] as ReflectionField; - expect(secondMember).toBeInstanceOf(ReflectionField); + const secondMember = identifier.members[1] as ESField; + expect(secondMember).toBeInstanceOf(ESField); expect(secondMember.name).toBe('value2'); - expect(secondMember.value).toBeInstanceOf(ReflectionExpression); + expect(secondMember.value).toBeInstanceOf(ESExpression); expect(secondMember.value?.definition).toBe('true'); }); @@ -398,20 +392,20 @@ describe('parser/Parser', () => const declaration = parser.parseDeclaration(DECLARATIONS.DESTRUCTURING_OBJECT); expect(declaration.name).toBe('{ key1 , key2 = false }'); - expect(declaration.value).toBeInstanceOf(ReflectionExpression); - expect(declaration.identifier).toBeInstanceOf(ReflectionDestructuredObject); + expect(declaration.value).toBeInstanceOf(ESExpression); + expect(declaration.identifier).toBeInstanceOf(ESDestructuredObject); - const identifier = declaration.identifier as ReflectionDestructuredArray; + const identifier = declaration.identifier as ESDestructuredArray; expect(identifier.members.length).toBe(2); - const firstMember = identifier.members[0] as ReflectionField; - expect(firstMember).toBeInstanceOf(ReflectionField); + const firstMember = identifier.members[0] as ESField; + expect(firstMember).toBeInstanceOf(ESField); expect(firstMember.name).toBe('key1'); - const secondMember = identifier.members[1] as ReflectionField; - expect(secondMember).toBeInstanceOf(ReflectionField); + const secondMember = identifier.members[1] as ESField; + expect(secondMember).toBeInstanceOf(ESField); expect(secondMember.name).toBe('key2'); - expect(secondMember.value).toBeInstanceOf(ReflectionExpression); + expect(secondMember.value).toBeInstanceOf(ESExpression); expect(secondMember.value?.definition).toBe('false'); }); @@ -420,7 +414,7 @@ describe('parser/Parser', () => const declaration = parser.parseDeclaration(DECLARATIONS.KEYWORD_AS_NAME); expect(declaration.name).toBe('as'); - expect(declaration.value).toBeInstanceOf(ReflectionExpression); + expect(declaration.value).toBeInstanceOf(ESExpression); expect(declaration.value?.definition).toBe("'value'"); }); @@ -429,7 +423,7 @@ describe('parser/Parser', () => const declaration = parser.parseDeclaration(DECLARATIONS.KEYWORD_AS_VALUE); expect(declaration.name).toBe('alias'); - expect(declaration.value).toBeInstanceOf(ReflectionExpression); + expect(declaration.value).toBeInstanceOf(ESExpression); expect(declaration.value?.definition).toBe('as'); }); }); @@ -520,7 +514,7 @@ describe('parser/Parser', () => { const funktion = parser.parseFunction(FUNCTIONS.GENERATOR); - expect(funktion).toBeInstanceOf(ReflectionGenerator); + expect(funktion).toBeInstanceOf(ESGenerator); expect(funktion.name).toBe('name'); expect(funktion.isAsync).toBe(false); expect(funktion.parameters.length).toBe(0); @@ -531,7 +525,7 @@ describe('parser/Parser', () => { const funktion = parser.parseFunction(FUNCTIONS.ASYNC_GENERATOR); - expect(funktion).toBeInstanceOf(ReflectionGenerator); + expect(funktion).toBeInstanceOf(ESGenerator); expect(funktion.name).toBe('name'); expect(funktion.isAsync).toBe(true); expect(funktion.parameters.length).toBe(0); @@ -542,7 +536,7 @@ describe('parser/Parser', () => { const funktion = parser.parseFunction(FUNCTIONS.EXPRESSION_GENERATOR); - expect(funktion).toBeInstanceOf(ReflectionGenerator); + expect(funktion).toBeInstanceOf(ESGenerator); expect(funktion.name).toBe('name'); expect(funktion.isAsync).toBe(false); expect(funktion.parameters.length).toBe(0); @@ -553,7 +547,7 @@ describe('parser/Parser', () => { const funktion = parser.parseFunction(FUNCTIONS.ASYNC_EXPRESSION_GENERATOR); - expect(funktion).toBeInstanceOf(ReflectionGenerator); + expect(funktion).toBeInstanceOf(ESGenerator); expect(funktion.name).toBe('name'); expect(funktion.isAsync).toBe(true); expect(funktion.parameters.length).toBe(0); @@ -570,13 +564,13 @@ describe('parser/Parser', () => const parameters = funktion.parameters; expect(parameters.length).toBe(2); - const first = parameters[0] as ReflectionField; - expect(first).toBeInstanceOf(ReflectionField); + const first = parameters[0] as ESField; + expect(first).toBeInstanceOf(ESField); expect(first.name).toBe('param1'); expect(first.value).toBe(undefined); - const second = parameters[1] as ReflectionField; - expect(second).toBeInstanceOf(ReflectionField); + const second = parameters[1] as ESField; + expect(second).toBeInstanceOf(ESField); expect(second.name).toBe('param2'); expect(second.value).toBe(undefined); }); @@ -591,15 +585,15 @@ describe('parser/Parser', () => const parameters = funktion.parameters; expect(parameters.length).toBe(2); - const first = parameters[0] as ReflectionField; - expect(first).toBeInstanceOf(ReflectionField); + const first = parameters[0] as ESField; + expect(first).toBeInstanceOf(ESField); expect(first.name).toBe('param1'); - expect(first.value).toEqual(new ReflectionExpression("'value1'")); + expect(first.value).toEqual(new ESExpression("'value1'")); - const second = parameters[1] as ReflectionField; - expect(second).toBeInstanceOf(ReflectionField); + const second = parameters[1] as ESField; + expect(second).toBeInstanceOf(ESField); expect(second.name).toBe('param2'); - expect(second.value).toEqual(new ReflectionExpression("true")); + expect(second.value).toEqual(new ESExpression("true")); }); it('should parse a function with a rest parameter', () => @@ -612,8 +606,8 @@ describe('parser/Parser', () => const parameters = funktion.parameters; expect(parameters.length).toBe(1); - const first = parameters[0] as ReflectionField; - expect(first).toBeInstanceOf(ReflectionField); + const first = parameters[0] as ESField; + expect(first).toBeInstanceOf(ESField); expect(first.name).toBe('...param1'); expect(first.value).toEqual(undefined); }); @@ -628,31 +622,31 @@ describe('parser/Parser', () => const parameters = funktion.parameters; expect(parameters.length).toBe(2); - const firstParameter = parameters[0] as ReflectionDestructuredObject; - expect(firstParameter).toBeInstanceOf(ReflectionDestructuredObject); + const firstParameter = parameters[0] as ESDestructuredObject; + expect(firstParameter).toBeInstanceOf(ESDestructuredObject); expect(firstParameter.members.length).toBe(2); - const firstMember = firstParameter.members[0] as ReflectionField; - expect(firstMember).toBeInstanceOf(ReflectionField); + const firstMember = firstParameter.members[0] as ESField; + expect(firstMember).toBeInstanceOf(ESField); expect(firstMember.name).toBe('param1'); expect(firstMember.value).toBe(undefined); - const secondMember = firstParameter.members[1] as ReflectionField; - expect(secondMember).toBeInstanceOf(ReflectionField); + const secondMember = firstParameter.members[1] as ESField; + expect(secondMember).toBeInstanceOf(ESField); expect(secondMember.name).toBe('param2'); expect(secondMember.value).toBe(undefined); - const secondParameter = parameters[1] as ReflectionDestructuredArray; - expect(secondParameter).toBeInstanceOf(ReflectionDestructuredArray); + const secondParameter = parameters[1] as ESDestructuredArray; + expect(secondParameter).toBeInstanceOf(ESDestructuredArray); expect(secondParameter.members.length).toBe(2); - const thirdMember = secondParameter.members[0] as ReflectionField; - expect(thirdMember).toBeInstanceOf(ReflectionField); + const thirdMember = secondParameter.members[0] as ESField; + expect(thirdMember).toBeInstanceOf(ESField); expect(thirdMember.name).toBe('param3'); expect(thirdMember.value).toBe(undefined); - const fourthMember = secondParameter.members[1] as ReflectionField; - expect(fourthMember).toBeInstanceOf(ReflectionField); + const fourthMember = secondParameter.members[1] as ESField; + expect(fourthMember).toBeInstanceOf(ESField); expect(fourthMember.name).toBe('param4'); expect(fourthMember.value).toBe(undefined); }); @@ -667,33 +661,33 @@ describe('parser/Parser', () => const parameters = funktion.parameters; expect(parameters.length).toBe(2); - const firstParameter = parameters[0] as ReflectionDestructuredObject; - expect(firstParameter).toBeInstanceOf(ReflectionDestructuredObject); + const firstParameter = parameters[0] as ESDestructuredObject; + expect(firstParameter).toBeInstanceOf(ESDestructuredObject); expect(firstParameter.members.length).toBe(2); - const firstMember = firstParameter.members[0] as ReflectionField; - expect(firstMember).toBeInstanceOf(ReflectionField); + const firstMember = firstParameter.members[0] as ESField; + expect(firstMember).toBeInstanceOf(ESField); expect(firstMember.name).toBe('param1'); - expect(firstMember.value).toBeInstanceOf(ReflectionExpression); + expect(firstMember.value).toBeInstanceOf(ESExpression); - const secondMember = firstParameter.members[1] as ReflectionField; - expect(secondMember).toBeInstanceOf(ReflectionField); + const secondMember = firstParameter.members[1] as ESField; + expect(secondMember).toBeInstanceOf(ESField); expect(secondMember.name).toBe('param2'); - expect(secondMember.value).toBeInstanceOf(ReflectionExpression); + expect(secondMember.value).toBeInstanceOf(ESExpression); - const secondParameter = parameters[1] as ReflectionDestructuredArray; - expect(secondParameter).toBeInstanceOf(ReflectionDestructuredArray); + const secondParameter = parameters[1] as ESDestructuredArray; + expect(secondParameter).toBeInstanceOf(ESDestructuredArray); expect(secondParameter.members.length).toBe(2); - const thirdMember = secondParameter.members[0] as ReflectionField; - expect(thirdMember).toBeInstanceOf(ReflectionField); + const thirdMember = secondParameter.members[0] as ESField; + expect(thirdMember).toBeInstanceOf(ESField); expect(thirdMember.name).toBe('param3'); - expect(thirdMember.value).toBeInstanceOf(ReflectionExpression); + expect(thirdMember.value).toBeInstanceOf(ESExpression); - const fourthMember = secondParameter.members[1] as ReflectionField; - expect(fourthMember).toBeInstanceOf(ReflectionField); + const fourthMember = secondParameter.members[1] as ESField; + expect(fourthMember).toBeInstanceOf(ESField); expect(fourthMember.name).toBe('param4'); - expect(fourthMember.value).toBeInstanceOf(ReflectionExpression); + expect(fourthMember.value).toBeInstanceOf(ESExpression); }); it('should parse a function with destructuring rest parameters', () => @@ -706,31 +700,31 @@ describe('parser/Parser', () => const parameters = funktion.parameters; expect(parameters.length).toBe(2); - const firstParameter = parameters[0] as ReflectionDestructuredObject; - expect(firstParameter).toBeInstanceOf(ReflectionDestructuredObject); + const firstParameter = parameters[0] as ESDestructuredObject; + expect(firstParameter).toBeInstanceOf(ESDestructuredObject); expect(firstParameter.members.length).toBe(2); - const firstMember = firstParameter.members[0] as ReflectionField; - expect(firstMember).toBeInstanceOf(ReflectionField); + const firstMember = firstParameter.members[0] as ESField; + expect(firstMember).toBeInstanceOf(ESField); expect(firstMember.name).toBe('param1'); expect(firstMember.value).toBe(undefined); - const secondMember = firstParameter.members[1] as ReflectionField; - expect(secondMember).toBeInstanceOf(ReflectionField); + const secondMember = firstParameter.members[1] as ESField; + expect(secondMember).toBeInstanceOf(ESField); expect(secondMember.name).toBe('param2'); expect(secondMember.value).toBe(undefined); - const secondParameter = parameters[1] as ReflectionDestructuredArray; - expect(secondParameter).toBeInstanceOf(ReflectionDestructuredArray); + const secondParameter = parameters[1] as ESDestructuredArray; + expect(secondParameter).toBeInstanceOf(ESDestructuredArray); expect(secondParameter.members.length).toBe(2); - const thirdMember = secondParameter.members[0] as ReflectionField; - expect(thirdMember).toBeInstanceOf(ReflectionField); + const thirdMember = secondParameter.members[0] as ESField; + expect(thirdMember).toBeInstanceOf(ESField); expect(thirdMember.name).toBe('param3'); expect(thirdMember.value).toBe(undefined); - const fourthMember = secondParameter.members[1] as ReflectionField; - expect(fourthMember).toBeInstanceOf(ReflectionField); + const fourthMember = secondParameter.members[1] as ESField; + expect(fourthMember).toBeInstanceOf(ESField); expect(fourthMember.name).toBe('...param4'); expect(fourthMember.value).toBe(undefined); }); diff --git a/packages/analysis/test/static/fixtures/classes.fixture.ts b/packages/analysis/test/static/fixtures/classes.fixture.ts new file mode 100644 index 00000000..89cd3421 --- /dev/null +++ b/packages/analysis/test/static/fixtures/classes.fixture.ts @@ -0,0 +1,53 @@ + +export const CLASSES = +{ + DECLARATION: "class Name {}", + EXTENDS: "class Name extends Parent {}", + EXPRESSION: "const name = class {}", + MEMBERS: `class Name +{ + #field1 = 'value1'; + field2; + + static #field3 = "value3"; + static field4; + + constructor(field1, ...field2) + { + this.#field1 = field1; + this.field2 = field2; + } + + get #getter1() { return field1; } + + get getter2() { return field2; } + + static get #getter3() { return field3; } + + static get getter4() { return field4; } + + set #setter1(value) { this.#field1 = value; } + + set setter2(value) { this.#field2 = value; } + + static set #setter3(value) { this.#field3 = value; } + + static set setter4(value) { this.#field4 = value; } + + method1() { return this.#field1; } + + async method2() { return this.#field1; } + + static method3() { return this.#field1; } + + static async method4() { return this.#field1; } + + #method5() { return this.#field1; } + + *generator1() { yield 1; } + + async *generator2() { yield 1; } + + static async *generator3() { yield 1; } +}` +}; diff --git a/packages/reflection/test/_fixtures/parser/Lexer.fixture.ts b/packages/analysis/test/static/fixtures/code.fixture.ts similarity index 95% rename from packages/reflection/test/_fixtures/parser/Lexer.fixture.ts rename to packages/analysis/test/static/fixtures/code.fixture.ts index 86d14c13..7b83c6f1 100644 --- a/packages/reflection/test/_fixtures/parser/Lexer.fixture.ts +++ b/packages/analysis/test/static/fixtures/code.fixture.ts @@ -1,5 +1,5 @@ -const CODE = +export const CODE = { OPERATORS: `=====!=+=*!=`, LITERALS: '`foo\\`ter`"bar\\"becue"\'baz\'', @@ -15,5 +15,3 @@ const CODE = REGEX_ARGUMENT: `doSomething(/[\\"]['"]/g)`, MINIFIED: 'return`foo`;identifier1=identifier2' }; - -export { CODE }; diff --git a/packages/analysis/test/static/fixtures/declarations.fixture.ts b/packages/analysis/test/static/fixtures/declarations.fixture.ts new file mode 100644 index 00000000..d8833d65 --- /dev/null +++ b/packages/analysis/test/static/fixtures/declarations.fixture.ts @@ -0,0 +1,17 @@ + +export const DECLARATIONS = +{ + EMPTY: "let name;", + CONST: "const name = 'const';", + LET: "let name = 'let';", + VAR: "var name = 'var';", + MULTIPLE: "let name1 = (1 + 2) * 3, name2, name3 = 'foo';", + EXPRESSION: `const number = new Number(Math.ceil(Math.random()) + 10).toString();`, + ARRAY: "const array = [ 'value1', 'value2' ];", + OBJECT: "const object = { key1: 'value1', key2: 'value2' };", + REGEX: "const regex = /regex/g;", + DESTRUCTURING_ARRAY: "const [value1, value2 = true] = array;", + DESTRUCTURING_OBJECT: "const {key1, key2 = false} = object;", + KEYWORD_AS_NAME: "const as = 'value';", + KEYWORD_AS_VALUE: "const alias = as;" +}; diff --git a/packages/analysis/test/static/fixtures/exports.fixture.ts b/packages/analysis/test/static/fixtures/exports.fixture.ts new file mode 100644 index 00000000..ccd5de02 --- /dev/null +++ b/packages/analysis/test/static/fixtures/exports.fixture.ts @@ -0,0 +1,15 @@ + +export const EXPORTS = +{ + EXPORT: "export { member }", + EXPORT_AS: "export { member as alias }", + EXPORT_DEFAULT: "export default name", + EXPORT_MULTIPLE: "export { name, member }", + EXPORT_MULTIPLE_AS: "export { name, member as alias }", + EXPORT_CLASS_DECLARATION: "export class name {}", + EXPORT_FIELD_DECLARATION: "export const name = 'value'", + EXPORT_FUNCTION_DECLARATION: "export function name() {}", + EXPORT_ASYNC_FUNCTION_DECLARATION: "export async function name() {}", + REEXPORT_ALL: "export * from 'module'", + REEXPORT_MEMBER: "export { member } from 'module'", +}; diff --git a/packages/analysis/test/static/fixtures/functions.fixture.ts b/packages/analysis/test/static/fixtures/functions.fixture.ts new file mode 100644 index 00000000..6c6bf95f --- /dev/null +++ b/packages/analysis/test/static/fixtures/functions.fixture.ts @@ -0,0 +1,25 @@ + +export const FUNCTIONS = +{ + DECLARATION: "function name() {}", + ASYNC_DECLARATION: "async function name() {}", + EXPRESSION: "const name = function() {}", + ASYNC_EXPRESSION: "const name = async function() {}", + ARROW: "const name = () => {}", + ARROW_EXPRESSION: "const name = () => 'value';", + ARROW_ARGUMENT: 'const name = arg => arg;', + ASYNC_ARROW: "const name = async () => {}", + GENERATOR: "function* name() {}", + ASYNC_GENERATOR: "async function* name() {}", + EXPRESSION_GENERATOR: "const name = function*() {}", + ASYNC_EXPRESSION_GENERATOR: "const name = async function*() {}", + PARAMETERS: "function name(param1, param2) {}", + DEFAULT_PARAMETERS: "function name(param1 = 'value1', param2 = true) {}", + REST_PARAMETERS: "function name(...param1) {}", + DESTRUCTURING_PARAMETERS: "function name({ param1, param2 }, [ param3, param4 ]) {}", + DESTRUCTURING_DEFAULT_PARAMETERS: "function name({ param1 = 'value1', param2 = true }, [ param3 = 'value3', param4 = true ]) {}", + DESTRUCTURING_REST_PARAMETERS: "function name({ param1, param2 }, [ param3, ...param4 ]) {}", + SIMPLE_BODY: "function name() { return 'value'; }", + BLOCK_BODY: "function name() { if (true) { return 'value'; } }", + KEYWORD_AS_NAME: "function as() {}" +}; diff --git a/packages/analysis/test/static/fixtures/imports.fixture.ts b/packages/analysis/test/static/fixtures/imports.fixture.ts new file mode 100644 index 00000000..a78b3d85 --- /dev/null +++ b/packages/analysis/test/static/fixtures/imports.fixture.ts @@ -0,0 +1,13 @@ + +export const IMPORTS = +{ + LOAD: "import 'module'", + IMPORT: "import { member } from 'module'", + IMPORT_AS: "import { member as alias } from 'module'", + IMPORT_ALL: "import * as name from 'module'", + IMPORT_DEFAULT: "import name from 'module'", + IMPORT_DEFAULT_MEMBER: "import name, { member } from 'module'", + IMPORT_DEFAULT_MEMBER_AS: "import name, { member as alias } from 'module'", + IMPORT_MULTIPLE_MEMBERS_AS: "import { name, member as alias } from 'module'", + IMPORT_DYNAMIC: "import('module')", +}; diff --git a/packages/analysis/test/static/fixtures/index.ts b/packages/analysis/test/static/fixtures/index.ts new file mode 100644 index 00000000..c87031d7 --- /dev/null +++ b/packages/analysis/test/static/fixtures/index.ts @@ -0,0 +1,9 @@ + +export * from './classes.fixture'; +export * from './code.fixture'; +export * from './declarations.fixture'; +export * from './exports.fixture'; +export * from './functions.fixture'; +export * from './imports.fixture'; +export * from './modules.fixture'; +export * from './values.fixture'; diff --git a/packages/analysis/test/static/fixtures/modules.fixture.ts b/packages/analysis/test/static/fixtures/modules.fixture.ts new file mode 100644 index 00000000..743f33a7 --- /dev/null +++ b/packages/analysis/test/static/fixtures/modules.fixture.ts @@ -0,0 +1,69 @@ + +export const MODULES = +{ + TERMINATED: +` +import { member } from 'module'; +import { member as alias } from 'module2'; + +const name = 'Peter' + ' van ' + 'Vliet'; + +export default function sum(a, b) { return a + b; } + +[1, 2, 3, 4, 5].sort((a, b) => a - b); + +try { sum(1, 2); } catch (error) { console.error(error); } + +export class Person +{ + #name; + #age; + + constructor(name, age) + { + this.#name = name; + this.#age = age; + } +} + +const peter = new Person(name, 42); + +async function async() { } +const a = async; +const b = async () => {}; + +const as = 12; +export { as as hi }; + +export { name, peter }; +`, + UNTERMINATED: +` + import { member } from 'module' + import { member as alias } from 'module2' + + const name = 'Peter' + ' van ' + 'Vliet' + + export default function sum(a, b) { return a + b } + + [1, 2, 3, 4, 5].sort((a, b) => a - b) + + try { sum(1, 2) } catch (error) { console.error(error) } + + export class Person + { + #name + #age + + constructor(name, age) + { + this.#name = name + this.#age = age + } + } + + const peter = new Person(name, 42) + + export { name, peter } +` +}; diff --git a/packages/analysis/test/static/fixtures/values.fixture.ts b/packages/analysis/test/static/fixtures/values.fixture.ts new file mode 100644 index 00000000..af2c9d6e --- /dev/null +++ b/packages/analysis/test/static/fixtures/values.fixture.ts @@ -0,0 +1,10 @@ + +export const VALUES = +{ + ARRAY: '[1, "foo", false, new Person("Peter", 42), { a: 1, b: 2 }]', + OBJECT: '{ key1: "value1", "key2": new Person().toString() }', + EXPRESSION:'new Number(Math.ceil(Math.random()) + 10).toString();', + EXPRESSION_GROUP: '(a + b) * c', + IF_ELSE: 'if (true) { return "value1"; } else { return "value2"; }', + TRY_CATCH_FINALLY: 'try { sum(1, 2); } catch (error) { console.error(error); } finally { console.log("finally"); }' +}; diff --git a/packages/caching/tsconfig.json b/packages/analysis/tsconfig.json similarity index 100% rename from packages/caching/tsconfig.json rename to packages/analysis/tsconfig.json diff --git a/packages/caching/vite.config.ts b/packages/analysis/vite.config.ts similarity index 100% rename from packages/caching/vite.config.ts rename to packages/analysis/vite.config.ts diff --git a/packages/reflection/CHANGELOG.md b/packages/build/CHANGELOG.md similarity index 100% rename from packages/reflection/CHANGELOG.md rename to packages/build/CHANGELOG.md diff --git a/packages/caching/README.md b/packages/build/README.md similarity index 50% rename from packages/caching/README.md rename to packages/build/README.md index 5528c804..1fa42ecb 100644 --- a/packages/caching/README.md +++ b/packages/build/README.md @@ -1,7 +1,7 @@ -# Jitar Caching +# Jitar Build -This package contains the components for creating the runtime cache for [Jitar](https://jitar.dev) applications. +This package provides the build process for the [Jitar](https://jitar.dev) runtime. For more information about Jitar: diff --git a/packages/build/package.json b/packages/build/package.json new file mode 100644 index 00000000..523beb74 --- /dev/null +++ b/packages/build/package.json @@ -0,0 +1,25 @@ +{ + "name": "@jitar/build", + "version": "0.7.4", + "description": "Application building library for the Jitar runtime.", + "author": "Masking Technology (https://jitar.dev)", + "license": "MIT", + "type": "module", + "types": "dist/index.d.ts", + "exports": "./dist/index.js", + "private": true, + "scripts": { + "test": "vitest run", + "test-coverage": "vitest run --coverage", + "lint": "eslint . --ext .ts", + "build": "tsc -p tsconfig.json", + "clean": "rm -rf dist" + }, + "dependencies": { + "@jitar/configuration": "*", + "@jitar/analysis": "*", + "@jitar/execution": "*", + "@jitar/sourcing": "*", + "@jitar/logging": "*" + } +} diff --git a/packages/build/src/BuildManager.ts b/packages/build/src/BuildManager.ts new file mode 100644 index 00000000..f6094f72 --- /dev/null +++ b/packages/build/src/BuildManager.ts @@ -0,0 +1,37 @@ + +import type { RuntimeConfiguration } from '@jitar/configuration'; +import { Files, LocalFileManager } from '@jitar/sourcing'; +import type { FileManager } from '@jitar/sourcing'; + +import { ApplicationReader } from './source'; +import { ApplicationBuilder } from './target'; + +export default class BuildManager +{ + #projectFileManager: FileManager; + #sourceFileManager: FileManager; + #targetFileManager: FileManager; + + #applicationReader: ApplicationReader; + #applicationBuilder: ApplicationBuilder; + + constructor(configuration: RuntimeConfiguration) + { + this.#projectFileManager = new LocalFileManager('./'); + this.#sourceFileManager = new LocalFileManager(configuration.source); + this.#targetFileManager = new LocalFileManager(configuration.target); + + this.#applicationReader = new ApplicationReader(this.#sourceFileManager); + this.#applicationBuilder = new ApplicationBuilder(this.#targetFileManager); + } + + async build(): Promise + { + const moduleFiles = await this.#sourceFileManager.filter(Files.MODULE_PATTERN); + const segmentFiles = await this.#projectFileManager.filter(Files.SEGMENT_PATTERN); + + const applicationModel = await this.#applicationReader.read(moduleFiles, segmentFiles); + + return this.#applicationBuilder.build(applicationModel); + } +} diff --git a/packages/build/src/index.ts b/packages/build/src/index.ts new file mode 100644 index 00000000..09fd95ee --- /dev/null +++ b/packages/build/src/index.ts @@ -0,0 +1,2 @@ + +export { default as BuildManager } from './BuildManager'; diff --git a/packages/build/src/source/application/Reader.ts b/packages/build/src/source/application/Reader.ts new file mode 100644 index 00000000..caef49e4 --- /dev/null +++ b/packages/build/src/source/application/Reader.ts @@ -0,0 +1,28 @@ + +import type { FileManager } from '@jitar/sourcing'; + +import { ModuleReader } from '../module'; +import { SegmentReader } from '../segment'; + +import Application from './models/Application.js'; + +export default class Reader +{ + #fileManager: FileManager; + + constructor(fileManager: FileManager) + { + this.#fileManager = fileManager; + } + + async read(moduleFiles: string[], segmentFiles: string[]): Promise + { + const moduleReader = new ModuleReader(this.#fileManager); + const repository = await moduleReader.readAll(moduleFiles); + + const segmentReader = new SegmentReader(this.#fileManager, repository); + const segmentation = await segmentReader.readAll(segmentFiles); + + return new Application(repository, segmentation); + } +} diff --git a/packages/build/src/source/application/index.ts b/packages/build/src/source/application/index.ts new file mode 100644 index 00000000..4c67bbdd --- /dev/null +++ b/packages/build/src/source/application/index.ts @@ -0,0 +1,3 @@ + +export { default as Application } from './models/Application'; +export { default as ApplicationReader } from './Reader'; diff --git a/packages/build/src/source/application/models/Application.ts b/packages/build/src/source/application/models/Application.ts new file mode 100644 index 00000000..7115d70e --- /dev/null +++ b/packages/build/src/source/application/models/Application.ts @@ -0,0 +1,19 @@ + +import type { ModuleRepository } from '../../module'; +import type { Segmentation } from '../../segment'; + +export default class Application +{ + #repository: ModuleRepository; + #segmentation: Segmentation; + + constructor(repository: ModuleRepository, segmentation: Segmentation) + { + this.#repository = repository; + this.#segmentation = segmentation; + } + + get repository() { return this.#repository; } + + get segmentation() { return this.#segmentation; } +} diff --git a/packages/build/src/source/index.ts b/packages/build/src/source/index.ts new file mode 100644 index 00000000..f2b22143 --- /dev/null +++ b/packages/build/src/source/index.ts @@ -0,0 +1,4 @@ + +export { Application, ApplicationReader } from './application'; +export { Module } from './module'; +export { Segment, SegmentModule, SegmentImplementation, Segmentation } from './segment'; diff --git a/packages/caching/src/building/ModuleReader.ts b/packages/build/src/source/module/Reader.ts similarity index 50% rename from packages/caching/src/building/ModuleReader.ts rename to packages/build/src/source/module/Reader.ts index dc1379db..40e4bac0 100644 --- a/packages/caching/src/building/ModuleReader.ts +++ b/packages/build/src/source/module/Reader.ts @@ -1,27 +1,35 @@ -import { Reflector } from '@jitar/reflection'; -import { FileManager } from '@jitar/runtime'; +import { Parser } from '@jitar/analysis'; +import type { FileManager } from '@jitar/sourcing'; -import ModuleFileNotLoaded from './errors/ModuleFileNotLoaded.js'; +import FileNotLoaded from './errors/FileNotLoaded'; -import Module from './models/Module.js'; +import Module from './models/Module'; +import Repository from './models/Repository'; -const reflector = new Reflector(); - -export default class ModuleReader +export default class Reader { #fileManager: FileManager; + #parser: Parser; - constructor(fileManager: FileManager) + constructor(fileManager: FileManager, parser: Parser = new Parser()) { this.#fileManager = fileManager; + this.#parser = parser; + } + + async readAll(filenames: string[]): Promise + { + const modules = await Promise.all(filenames.map(filename => this.read(filename))); + + return new Repository(modules); } async read(filename: string): Promise { const relativeLocation = this.#fileManager.getRelativeLocation(filename); const code = await this.#loadCode(filename); - const module = reflector.parse(code); + const module = this.#parser.parse(code); return new Module(relativeLocation, code, module); } @@ -38,7 +46,7 @@ export default class ModuleReader { const message = error instanceof Error ? error.message : String(error); - throw new ModuleFileNotLoaded(filename, message); + throw new FileNotLoaded(filename, message); } } } diff --git a/packages/caching/src/building/errors/ModuleFileNotLoaded.ts b/packages/build/src/source/module/errors/FileNotLoaded.ts similarity index 73% rename from packages/caching/src/building/errors/ModuleFileNotLoaded.ts rename to packages/build/src/source/module/errors/FileNotLoaded.ts index c987b99f..e7b9091d 100644 --- a/packages/caching/src/building/errors/ModuleFileNotLoaded.ts +++ b/packages/build/src/source/module/errors/FileNotLoaded.ts @@ -1,5 +1,5 @@ -export default class ModuleFileNotLoaded extends Error +export default class FileNotLoaded extends Error { constructor(filename: string, message: string) { diff --git a/packages/build/src/source/module/index.ts b/packages/build/src/source/module/index.ts new file mode 100644 index 00000000..21f96b8c --- /dev/null +++ b/packages/build/src/source/module/index.ts @@ -0,0 +1,5 @@ + +export { default as Module } from './models/Module'; +export { default as ModuleRepository } from './models/Repository'; + +export { default as ModuleReader } from './Reader'; diff --git a/packages/caching/src/building/models/Module.ts b/packages/build/src/source/module/models/Module.ts similarity index 50% rename from packages/caching/src/building/models/Module.ts rename to packages/build/src/source/module/models/Module.ts index 799f6ca0..067047ae 100644 --- a/packages/caching/src/building/models/Module.ts +++ b/packages/build/src/source/module/models/Module.ts @@ -1,22 +1,22 @@ -import { ReflectionModule } from '@jitar/reflection'; +import type { ESModule } from '@jitar/analysis'; export default class Module { #filename: string; #code: string; - #content: ReflectionModule; + #model: ESModule; - constructor(filename: string, code: string, content: ReflectionModule) + constructor(filename: string, code: string, model: ESModule) { this.#code = code; this.#filename = filename; - this.#content = content; + this.#model = model; } get filename() { return this.#filename; } get code() { return this.#code; } - get content() { return this.#content; } + get model() { return this.#model; } } diff --git a/packages/build/src/source/module/models/Repository.ts b/packages/build/src/source/module/models/Repository.ts new file mode 100644 index 00000000..fbecacfd --- /dev/null +++ b/packages/build/src/source/module/models/Repository.ts @@ -0,0 +1,19 @@ + +import type Module from './Module'; + +export default class Repository +{ + #modules: Module[]; + + constructor(modules: Module[]) + { + this.#modules = modules; + } + + get modules() { return this.#modules; } + + get(filename: string): Module | undefined + { + return this.#modules.find(module => module.filename === filename); + } +} diff --git a/packages/build/src/source/segment/Reader.ts b/packages/build/src/source/segment/Reader.ts new file mode 100644 index 00000000..6a4b2b50 --- /dev/null +++ b/packages/build/src/source/segment/Reader.ts @@ -0,0 +1,237 @@ + +import { ESFunction, ESClass, ESMember } from '@jitar/analysis'; +import type { FileManager } from '@jitar/sourcing'; + +import { FileHelper, IdGenerator } from '../../utils'; +import type { ModuleRepository } from '../module'; + +import FunctionNotAsync from './errors/FunctionNotAsync'; +import InvalidFilename from './errors/InvalidFilename'; +import MissingModuleExport from './errors/MissingModuleExport'; +import FileNotLoaded from './errors/FileNotLoaded'; +import InvalidModuleExport from './errors/InvalidModuleExport'; +import ModuleNotFound from './errors/ModuleNotFound'; + +import Segmentation from './models/Segmentation'; +import Segment from './models/Segment'; +import Module from './models/Module'; +import Class from './models/Class'; +import Procedure from './models/Procedure'; +import Implementation from './models/Implementation'; + +import SegmentFile from './types/File'; + +type Members = +{ + classes: Map; + procedures: Map +} + +type MemberProperties = +{ + id: string; + importKey: string; + name: string; + access: string; + version: string; + fqn: string; +} + +const SEGMENT_FILE_EXTENSION = '.segment.json'; +const DEFAULT_ACCESS_LEVEL = 'private'; +const DEFAULT_VERSION_NUMBER = '0.0.0'; + +export default class SegmentReader +{ + #fileManager: FileManager; + #repository: ModuleRepository; + + #fileHelper = new FileHelper(); + + constructor(fileManager: FileManager, repository: ModuleRepository) + { + this.#fileManager = fileManager; + this.#repository = repository; + } + + async readAll(filenames: string[]): Promise + { + const segments = await Promise.all(filenames.map(filename => this.read(filename))); + + return new Segmentation(segments); + } + + async read(filename: string): Promise + { + const definition = await this.#loadSegmentDefinition(filename); + + const name = this.#extractSegmentName(filename); + const modules = this.#createModules(definition); + const members = this.#createMembers(modules); + + const classes = [...members.classes.values()]; + const procedures = [...members.procedures.values()]; + + return new Segment(name, modules, classes, procedures); + } + + #extractSegmentName(filename: string): string + { + const file = filename.split('/').pop(); + + if (file === undefined || file === '') + { + throw new InvalidFilename(filename); + } + + return file.replace(SEGMENT_FILE_EXTENSION, ''); + } + + async #loadSegmentDefinition(filename: string): Promise + { + try + { + const content = await this.#fileManager.getContent(filename); + + return JSON.parse(content.toString()) as SegmentFile; + } + catch (error: unknown) + { + const message = error instanceof Error ? error.message : String(error); + + throw new FileNotLoaded(filename, message); + } + } + + #createModules(definition: SegmentFile): Module[] + { + const modules: Module[] = []; + + for (const [filename, moduleImports] of Object.entries(definition)) + { + const moduleFilename = this.#makeModuleFilename(filename); + const location = this.#extractLocation(moduleFilename); + + const module = new Module(moduleFilename, location, moduleImports); + + modules.push(module); + } + + return modules; + } + + #makeModuleFilename(filename: string): string + { + const fullFilename = this.#fileHelper.assureExtension(filename); + + if (fullFilename.startsWith('./')) return fullFilename.substring(2); + if (fullFilename.startsWith('/')) return fullFilename.substring(1); + + return fullFilename; + } + + #extractLocation(filename: string): string + { + const moduleParts = filename.split('/'); + moduleParts.pop(); + + return moduleParts.join('/'); + } + + #createMembers(modules: Module[]): Members + { + const members: Members = { classes: new Map(), procedures: new Map() }; + + const idGenerator = new IdGenerator(); + + for (const module of modules) + { + this.#extractModuleMembers(module, members, idGenerator); + } + + return members; + } + + #extractModuleMembers(module: Module, members: Members, idGenerator: IdGenerator): void + { + for (const importKey in module.imports) + { + const id = idGenerator.next(); + const model = this.#getMember(module.filename, importKey); + + const properties = module.imports[importKey]; + + const name = properties.as ?? model.name; + const access = properties.access ?? DEFAULT_ACCESS_LEVEL; + const version = properties.version ?? DEFAULT_VERSION_NUMBER; + + const fqn = module.location !== '' ? `${module.location}/${name}` : name; + + const memberProperties = { id, importKey, name, access, version, fqn }; + + if (model instanceof ESClass) + { + this.#registerClassMember(module, members, model, memberProperties); + + continue; + } + + if (model instanceof ESFunction) + { + this.#registerProcedureMember(module, members, model, memberProperties); + + continue; + } + + throw new InvalidModuleExport(module.filename, importKey); + } + } + + #registerClassMember(module: Module, members: Members, model: ESClass, properties: MemberProperties): void + { + const clazz = new Class(properties.id, properties.importKey, properties.fqn, model); + + module.addMember(clazz); + + members.classes.set(properties.fqn, clazz); + } + + #registerProcedureMember(module: Module, members: Members, model: ESFunction, properties: MemberProperties): void + { + if (model.isAsync === false) + { + throw new FunctionNotAsync(module.filename, properties.name); + } + + const implementation = new Implementation(properties.id, properties.importKey, properties.fqn, properties.access, properties.version, model); + + module.addMember(implementation); + + const procedure = members.procedures.has(implementation.fqn) + ? members.procedures.get(implementation.fqn) as Procedure + : new Procedure(implementation.fqn); + + procedure.addImplementation(implementation); + + members.procedures.set(implementation.fqn, procedure); + } + + #getMember(filename: string, importKey: string): ESMember + { + const module = this.#repository.get(filename); + + if (module === undefined) + { + throw new ModuleNotFound(filename); + } + + const member = module.model.getExported(importKey); + + if (member === undefined) + { + throw new MissingModuleExport(filename, importKey); + } + + return member; + } +} diff --git a/packages/caching/src/building/errors/DuplicateImplementation.ts b/packages/build/src/source/segment/errors/DuplicateImplementation.ts similarity index 100% rename from packages/caching/src/building/errors/DuplicateImplementation.ts rename to packages/build/src/source/segment/errors/DuplicateImplementation.ts diff --git a/packages/caching/src/building/errors/SegmentFileNotLoaded.ts b/packages/build/src/source/segment/errors/FileNotLoaded.ts similarity index 72% rename from packages/caching/src/building/errors/SegmentFileNotLoaded.ts rename to packages/build/src/source/segment/errors/FileNotLoaded.ts index edb62d63..0ead9e57 100644 --- a/packages/caching/src/building/errors/SegmentFileNotLoaded.ts +++ b/packages/build/src/source/segment/errors/FileNotLoaded.ts @@ -1,5 +1,5 @@ -export default class SegmentFileNotLoaded extends Error +export default class FileNotLoaded extends Error { constructor(filename: string, message: string) { diff --git a/packages/caching/src/building/errors/FunctionNotAsync.ts b/packages/build/src/source/segment/errors/FunctionNotAsync.ts similarity index 100% rename from packages/caching/src/building/errors/FunctionNotAsync.ts rename to packages/build/src/source/segment/errors/FunctionNotAsync.ts diff --git a/packages/caching/src/building/errors/InvalidSegmentFilename.ts b/packages/build/src/source/segment/errors/InvalidFilename.ts similarity index 65% rename from packages/caching/src/building/errors/InvalidSegmentFilename.ts rename to packages/build/src/source/segment/errors/InvalidFilename.ts index 94f9184f..42ea9194 100644 --- a/packages/caching/src/building/errors/InvalidSegmentFilename.ts +++ b/packages/build/src/source/segment/errors/InvalidFilename.ts @@ -1,5 +1,5 @@ -export default class InvalidSegmentFilename extends Error +export default class InvalidFilename extends Error { constructor(filename: string) { diff --git a/packages/build/src/source/segment/errors/InvalidModuleExport.ts b/packages/build/src/source/segment/errors/InvalidModuleExport.ts new file mode 100644 index 00000000..ac2d9cb0 --- /dev/null +++ b/packages/build/src/source/segment/errors/InvalidModuleExport.ts @@ -0,0 +1,8 @@ + +export default class InvalidModuleExport extends Error +{ + constructor(filename: string, exportKey: string) + { + super(`The export '${exportKey}' from file '${filename}' is not a function or a class.`); + } +} diff --git a/packages/caching/src/building/errors/MissingModuleExport.ts b/packages/build/src/source/segment/errors/MissingModuleExport.ts similarity index 100% rename from packages/caching/src/building/errors/MissingModuleExport.ts rename to packages/build/src/source/segment/errors/MissingModuleExport.ts diff --git a/packages/build/src/source/segment/errors/ModuleNotFound.ts b/packages/build/src/source/segment/errors/ModuleNotFound.ts new file mode 100644 index 00000000..58d0f902 --- /dev/null +++ b/packages/build/src/source/segment/errors/ModuleNotFound.ts @@ -0,0 +1,8 @@ + +export default class ModuleNotLoaded extends Error +{ + constructor(filename: string) + { + super(`Segmented module not found '${filename}'`); + } +} diff --git a/packages/build/src/source/segment/index.ts b/packages/build/src/source/segment/index.ts new file mode 100644 index 00000000..3aefb4d0 --- /dev/null +++ b/packages/build/src/source/segment/index.ts @@ -0,0 +1,8 @@ + +export { default as Segment } from './models/Segment'; +export { default as SegmentModule } from './models/Module'; +export { default as SegmentProcedure } from './models/Procedure'; +export { default as SegmentImplementation } from './models/Implementation'; +export { default as Segmentation } from './models/Segmentation'; + +export { default as SegmentReader } from './Reader'; diff --git a/packages/build/src/source/segment/models/Class.ts b/packages/build/src/source/segment/models/Class.ts new file mode 100644 index 00000000..b82bcd08 --- /dev/null +++ b/packages/build/src/source/segment/models/Class.ts @@ -0,0 +1,18 @@ + +import { ESClass } from '@jitar/analysis'; + +import Member from './Member'; + +export default class Class extends Member +{ + #model: ESClass; + + constructor(id: string, importKey: string, fqn: string, model: ESClass) + { + super(id, importKey, fqn); + + this.#model = model; + } + + get model() { return this.#model; } +} diff --git a/packages/build/src/source/segment/models/Implementation.ts b/packages/build/src/source/segment/models/Implementation.ts new file mode 100644 index 00000000..b0f72cfd --- /dev/null +++ b/packages/build/src/source/segment/models/Implementation.ts @@ -0,0 +1,26 @@ + +import type { ESFunction } from '@jitar/analysis'; + +import Member from './Member'; + +export default class Implementation extends Member +{ + #access: string; + #version: string; + #model: ESFunction; + + constructor(id: string, importKey: string, fqn: string, access: string, version: string, model: ESFunction) + { + super(id, importKey, fqn); + + this.#access = access; + this.#version = version; + this.#model = model; + } + + get access() { return this.#access; } + + get version() { return this.#version; } + + get model() { return this.#model; } +} diff --git a/packages/caching/src/building/models/SegmentImport.ts b/packages/build/src/source/segment/models/Import.ts similarity index 87% rename from packages/caching/src/building/models/SegmentImport.ts rename to packages/build/src/source/segment/models/Import.ts index f7d96fa5..76af24f8 100644 --- a/packages/caching/src/building/models/SegmentImport.ts +++ b/packages/build/src/source/segment/models/Import.ts @@ -1,5 +1,5 @@ -export default class SegmentImport +export default class Import { #members: string[]; #from: string; diff --git a/packages/build/src/source/segment/models/Member.ts b/packages/build/src/source/segment/models/Member.ts new file mode 100644 index 00000000..a8922790 --- /dev/null +++ b/packages/build/src/source/segment/models/Member.ts @@ -0,0 +1,20 @@ + +export default class Member +{ + #id: string; + #importKey: string; + #fqn: string; + + public constructor(id: string, importKey: string, fqn: string) + { + this.#id = id; + this.#importKey = importKey; + this.#fqn = fqn; + } + + public get id() { return this.#id; } + + public get importKey() { return this.#importKey; } + + public get fqn() { return this.#fqn; } +} diff --git a/packages/build/src/source/segment/models/Module.ts b/packages/build/src/source/segment/models/Module.ts new file mode 100644 index 00000000..3b8bcddb --- /dev/null +++ b/packages/build/src/source/segment/models/Module.ts @@ -0,0 +1,54 @@ + +import type Imports from '../types/Imports'; + +import Class from './Class'; +import Implementation from './Implementation'; +import type Member from './Member'; + +export default class Module +{ + #filename: string; + #location: string; + #imports: Imports; + #members: Member[] = []; + + constructor(filename: string, location: string, imports: Imports) + { + this.#filename = filename; + this.#location = location; + this.#imports = imports; + } + + get filename() { return this.#filename; } + + get location() { return this.#location; } + + get imports() { return this.#imports; } + + get members() { return this.#members; } + + hasClasses(): boolean + { + return this.#members.some(member => member instanceof Class); + } + + getClasses(): Class[] + { + return this.#members.filter(member => member instanceof Class) as Class[]; + } + + hasImplementations(): boolean + { + return this.#members.some(member => member instanceof Implementation); + } + + getImplementations(): Implementation[] + { + return this.#members.filter(member => member instanceof Implementation) as Implementation[]; + } + + addMember(members: Member): void + { + this.#members.push(members); + } +} diff --git a/packages/build/src/source/segment/models/Procedure.ts b/packages/build/src/source/segment/models/Procedure.ts new file mode 100644 index 00000000..0641bbf1 --- /dev/null +++ b/packages/build/src/source/segment/models/Procedure.ts @@ -0,0 +1,23 @@ + +import type Implementation from './Implementation'; + +export default class Procedure +{ + #fqn: string; + #implementations: Implementation[] = []; + + constructor(fqn: string, implementations: Implementation[] = []) + { + this.#fqn = fqn; + this.#implementations = implementations; + } + + get fqn() { return this.#fqn; } + + get implementations() { return this.#implementations; } + + addImplementation(implementation: Implementation): void + { + this.#implementations.push(implementation); + } +} diff --git a/packages/build/src/source/segment/models/Segment.ts b/packages/build/src/source/segment/models/Segment.ts new file mode 100644 index 00000000..1408c06f --- /dev/null +++ b/packages/build/src/source/segment/models/Segment.ts @@ -0,0 +1,48 @@ + +import type Module from './Module'; +import type Class from './Class'; +import type Procedure from './Procedure'; + +export default class Segment +{ + #name: string; + #modules: Module[]; + #classes: Class[]; + #procedures: Procedure[]; + + constructor(name: string, modules: Module[], classes: Class[], procedures: Procedure[]) + { + this.#name = name; + this.#modules = modules; + this.#classes = classes; + this.#procedures = procedures; + } + + get name() { return this.#name; } + + get modules() { return this.#modules; } + + get classes() { return this.#classes; } + + get procedures() { return this.#procedures; } + + hasModule(filename: string): boolean + { + return this.#modules.some(module => module.filename === filename); + } + + getModule(filename: string): Module | undefined + { + return this.#modules.find(module => module.filename === filename); + } + + hasProcedure(fqn: string): boolean + { + return this.#procedures.some(procedure => procedure.fqn === fqn); + } + + getProcedure(fqn: string): Procedure | undefined + { + return this.#procedures.find(procedure => procedure.fqn === fqn); + } +} diff --git a/packages/build/src/source/segment/models/Segmentation.ts b/packages/build/src/source/segment/models/Segmentation.ts new file mode 100644 index 00000000..e4698497 --- /dev/null +++ b/packages/build/src/source/segment/models/Segmentation.ts @@ -0,0 +1,29 @@ + +import type Segment from './Segment'; + +export default class Segmentation +{ + #segments: Segment[]; + + constructor(segments: Segment[]) + { + this.#segments = segments; + } + + get segments() { return this.#segments; } + + getSegment(segmentName: string): Segment | undefined + { + return this.#segments.find(segment => segment.name === segmentName); + } + + isModuleSegmented(moduleFilename: string): boolean + { + return this.#segments.some(segment => segment.hasModule(moduleFilename)); + } + + getSegments(moduleFilename: string): Segment[] + { + return this.#segments.filter(segment => segment.hasModule(moduleFilename)); + } +} diff --git a/packages/build/src/source/segment/types/File.ts b/packages/build/src/source/segment/types/File.ts new file mode 100644 index 00000000..0d9ac5b4 --- /dev/null +++ b/packages/build/src/source/segment/types/File.ts @@ -0,0 +1,6 @@ + +import SegmentImports from './Imports'; + +type File = Record; + +export default File; diff --git a/packages/build/src/source/segment/types/ImportProperties.ts b/packages/build/src/source/segment/types/ImportProperties.ts new file mode 100644 index 00000000..f9a6462c --- /dev/null +++ b/packages/build/src/source/segment/types/ImportProperties.ts @@ -0,0 +1,9 @@ + +type ImportProperties = +{ + as?: string; + access?: string; + version?: string; +} + +export default ImportProperties; diff --git a/packages/build/src/source/segment/types/Imports.ts b/packages/build/src/source/segment/types/Imports.ts new file mode 100644 index 00000000..017b9478 --- /dev/null +++ b/packages/build/src/source/segment/types/Imports.ts @@ -0,0 +1,6 @@ + +import ImportProperties from './ImportProperties'; + +type Imports = Record; + +export default Imports; diff --git a/packages/build/src/target/Builder.ts b/packages/build/src/target/Builder.ts new file mode 100644 index 00000000..465d6fad --- /dev/null +++ b/packages/build/src/target/Builder.ts @@ -0,0 +1,27 @@ + +import type { FileManager } from '@jitar/sourcing'; + +import type { Application } from '../source'; + +import ModuleBuilder from './ModuleBuilder'; +import SegmentBuilder from './SegmentBuilder'; + +export default class Builder +{ + #moduleBuilder: ModuleBuilder; + #segmentBuilder: SegmentBuilder; + + constructor(fileManager: FileManager) + { + this.#moduleBuilder = new ModuleBuilder(fileManager); + this.#segmentBuilder = new SegmentBuilder(fileManager); + } + + async build(application: Application): Promise + { + await Promise.all([ + this.#moduleBuilder.build(application), + this.#segmentBuilder.build(application) + ]); + } +} diff --git a/packages/build/src/target/ExportRewriter.ts b/packages/build/src/target/ExportRewriter.ts new file mode 100644 index 00000000..d3626036 --- /dev/null +++ b/packages/build/src/target/ExportRewriter.ts @@ -0,0 +1,150 @@ + +import { Parser } from '@jitar/analysis'; +import type { ESExport } from '@jitar/analysis'; + +import type { Module, Segmentation, Segment } from '../source'; +import { FileHelper } from '../utils'; + +const EXPORTS_ALL = '*'; + +const EXPORT_PATTERN = /export\s(?:["'\s]*([\w*{}\n, ]+)from\s*)?["'\s]*([@\w/._-]+)["'\s].*/g; +const APPLICATION_MODULE_INDICATORS = ['.', '/', 'http:', 'https:']; + +export default class ExportRewriter +{ + #module: Module; + #segmentation: Segmentation; + #segment: Segment | undefined; + + #parser = new Parser(); + #fileHelper = new FileHelper(); + + constructor(module: Module, segmentation: Segmentation, segment?: Segment) + { + this.#module = module; + this.#segmentation = segmentation; + this.#segment = segment; + } + + rewrite(code: string): string + { + const replacer = (statement: string) => this.#replaceExport(statement); + + return code.replaceAll(EXPORT_PATTERN, replacer); + } + + #replaceExport(statement: string): string + { + const dependency = this.#parser.parseExport(statement); + + if (dependency.from === undefined) + { + return statement; + } + + return this.#isApplicationModule(dependency) + ? this.#rewriteApplicationExport(dependency) + : this.#rewriteRuntimeExport(dependency); + } + + #isApplicationModule(dependency: ESExport): boolean + { + return APPLICATION_MODULE_INDICATORS.some(indicator => dependency.from!.startsWith(indicator, 1)); + } + + #rewriteApplicationExport(dependency: ESExport): string + { + const targetModuleFilename = this.#getTargetModuleFilename(dependency); + + if (this.#segmentation.isModuleSegmented(targetModuleFilename)) + { + // export segmented module + + if (this.#segment?.hasModule(targetModuleFilename)) + { + const from = this.#rewriteApplicationFrom(targetModuleFilename, this.#segment.name); + + return this.#rewriteToStaticExport(dependency, from); // same segment + } + + console.warn('Exporting a module from another segment!'); + + const from = this.#rewriteApplicationFrom(targetModuleFilename, 'remote'); + + return this.#rewriteToStaticExport(dependency, from); // different segments + } + + // export shared (unsegmented) module + + if (this.#segment !== undefined) + { + console.warn('Exporting shared module from a segmented module!'); + } + + const from = this.#rewriteApplicationFrom(targetModuleFilename, 'shared'); + + return this.#rewriteToStaticExport(dependency, from); + } + + #rewriteRuntimeExport(dependency: ESExport): string + { + const from = this.#rewriteRuntimeFrom(dependency); + + return this.#rewriteToStaticExport(dependency, from); + } + + #rewriteApplicationFrom(filename: string, scope: string): string + { + const callingModulePath = this.#fileHelper.extractPath(this.#module.filename); + const relativeFilename = this.#fileHelper.makePathRelative(filename, callingModulePath); + + return this.#fileHelper.addSubExtension(relativeFilename, scope); + } + + #rewriteRuntimeFrom(dependency: ESExport): string + { + return this.#stripFrom(dependency.from!); + } + + #rewriteToStaticExport(dependency: ESExport, from: string): string + { + if (dependency.members.length === 0) + { + return `export "${from}";`; + } + + const members = this.#rewriteStaticExportMembers(dependency); + + return `export ${members} from "${from}";`; + } + + #rewriteStaticExportMembers(dependency: ESExport): string + { + const members = dependency.members; + + if (members.length === 1 && members[0].name === '') + { + const member = members[0]; + + return member.name !== member.as ? `${EXPORTS_ALL} as ${member.as}` : EXPORTS_ALL; + } + + const memberExports = members.map(member => member.name !== member.as ? `${member.name} as ${member.as}` : member.name); + + return `{ ${memberExports.join(', ')} }`; + } + + #getTargetModuleFilename(dependency: ESExport): string + { + const from = this.#stripFrom(dependency.from!); + const callingModulePath = this.#fileHelper.extractPath(this.#module.filename); + const translated = this.#fileHelper.makePathAbsolute(from, callingModulePath); + + return this.#fileHelper.assureExtension(translated); + } + + #stripFrom(from: string): string + { + return from.substring(1, from.length - 1); + } +} diff --git a/packages/build/src/target/ImportRewriter.ts b/packages/build/src/target/ImportRewriter.ts new file mode 100644 index 00000000..30714633 --- /dev/null +++ b/packages/build/src/target/ImportRewriter.ts @@ -0,0 +1,170 @@ + +import { Parser } from '@jitar/analysis'; +import type { ESImport } from '@jitar/analysis'; + +import type { Module, Segmentation, Segment } from '../source'; +import { FileHelper } from '../utils'; + +const KEYWORD_DEFAULT = 'default'; +const IMPORT_PATTERN = /import\s(?:["'\s]*([\w*{}\n, ]+)from\s*)?["'\s]*([@\w/._-]+)["'\s].*/g; +const APPLICATION_MODULE_INDICATORS = ['.', '/', 'http:', 'https:']; + +export default class ImportRewriter +{ + #module: Module; + #segmentation: Segmentation; + #segment: Segment | undefined; + + #parser = new Parser(); + #fileHelper = new FileHelper(); + + constructor(module: Module, segmentation: Segmentation, segment?: Segment) + { + this.#module = module; + this.#segmentation = segmentation; + this.#segment = segment; + } + + rewrite(code: string): string + { + const replacer = (statement: string) => this.#replaceImport(statement); + + return code.replaceAll(IMPORT_PATTERN, replacer); + } + + #replaceImport(statement: string): string + { + const dependency = this.#parser.parseImport(statement); + + return this.#isApplicationModule(dependency) + ? this.#rewriteApplicationImport(dependency) + : this.#rewriteRuntimeImport(dependency); + } + + #isApplicationModule(dependency: ESImport): boolean + { + return APPLICATION_MODULE_INDICATORS.some(indicator => dependency.from.startsWith(indicator, 1)); + } + + #rewriteApplicationImport(dependency: ESImport): string + { + const targetModuleFilename = this.#getTargetModuleFilename(dependency); + + if (this.#segmentation.isModuleSegmented(targetModuleFilename)) + { + // import segmented module + + if (this.#segment?.hasModule(targetModuleFilename)) + { + const from = this.#rewriteApplicationFrom(targetModuleFilename, this.#segment.name); + + return this.#rewriteToStaticImport(dependency, from); // same segment + } + + const from = this.#rewriteApplicationFrom(targetModuleFilename, 'remote'); + + return this.#rewriteToStaticImport(dependency, from); // different segments + } + + // import shared (unsegmented) module + + const from = this.#rewriteApplicationFrom(targetModuleFilename, 'shared'); + + return this.#segment === undefined + ? this.#rewriteToStaticImport(dependency, from) // shared to shared + : this.#rewriteToDynamicImport(dependency, from); // segmented to shared (prevent bundling) + } + + #rewriteRuntimeImport(dependency: ESImport): string + { + const from = this.#rewriteRuntimeFrom(dependency); + + return this.#rewriteToStaticImport(dependency, from); + } + + #rewriteApplicationFrom(filename: string, scope: string): string + { + const callingModulePath = this.#fileHelper.extractPath(this.#module.filename); + const relativeFilename = this.#fileHelper.makePathRelative(filename, callingModulePath); + + return this.#fileHelper.addSubExtension(relativeFilename, scope); + } + + #rewriteRuntimeFrom(dependency: ESImport): string + { + return this.#stripFrom(dependency.from); + } + + #rewriteToStaticImport(dependency: ESImport, from: string): string + { + if (dependency.members.length === 0) + { + return `import "${from}";`; + } + + const members = this.#rewriteStaticImportMembers(dependency); + + return `import ${members} from "${from}";`; + } + + #rewriteToDynamicImport(dependency: ESImport, from: string): string + { + if (dependency.members.length === 0) + { + return `await import("${from}");`; + } + + const members = this.#rewriteDynamicImportMembers(dependency); + + return `const ${members} = await import("${from}");`; + } + + #rewriteStaticImportMembers(dependency: ESImport): string + { + const defaultMember = dependency.members.find(member => member.name === KEYWORD_DEFAULT); + const hasDefaultMember = defaultMember !== undefined; + const defaultMemberImport = hasDefaultMember ? defaultMember.as : ''; + + const namedMembers = dependency.members.filter(member => member.name !== KEYWORD_DEFAULT); + const namedMemberImports = namedMembers.map(member => member.name !== member.as ? `${member.name} as ${member.as}` : member.name); + const hasNamedMembers = namedMemberImports.length > 0; + const groupedNamedMemberImports = hasNamedMembers ? `{ ${namedMemberImports.join(', ')} }` : ''; + + const separator = hasDefaultMember && hasNamedMembers ? ', ' : ''; + + return `${defaultMemberImport}${separator}${groupedNamedMemberImports}`; + } + + #rewriteDynamicImportMembers(dependency: ESImport): string + { + if (this.#doesImportAll(dependency)) + { + return dependency.members[0].as; + } + + const members = dependency.members; + const memberImports = members.map(member => member.name !== member.as ? `${member.name} : ${member.as}` : member.name); + + return `{ ${memberImports.join(', ')} }`; + } + + #getTargetModuleFilename(dependency: ESImport): string + { + const from = this.#stripFrom(dependency.from); + const callingModulePath = this.#fileHelper.extractPath(this.#module.filename); + const translated = this.#fileHelper.makePathAbsolute(from, callingModulePath); + + return this.#fileHelper.assureExtension(translated); + } + + #doesImportAll(dependency: ESImport): boolean + { + return dependency.members.length === 1 + && dependency.members[0].name === '*'; + } + + #stripFrom(from: string): string + { + return from.substring(1, from.length - 1); + } +} diff --git a/packages/build/src/target/LocalModuleBuilder.ts b/packages/build/src/target/LocalModuleBuilder.ts new file mode 100644 index 00000000..17e8ca49 --- /dev/null +++ b/packages/build/src/target/LocalModuleBuilder.ts @@ -0,0 +1,18 @@ + +import { Module, Segmentation, Segment } from '../source'; + +import ImportRewriter from './ImportRewriter'; +import ExportRewriter from './ExportRewriter'; + +export default class LocalModuleBuilder +{ + build(module: Module, segmentation: Segmentation, segment?: Segment): string + { + const importRewriter = new ImportRewriter(module, segmentation, segment); + const exportRewriter = new ExportRewriter(module, segmentation, segment); + + const importCode = importRewriter.rewrite(module.code); + + return exportRewriter.rewrite(importCode); + } +} diff --git a/packages/build/src/target/ModuleBuilder.ts b/packages/build/src/target/ModuleBuilder.ts new file mode 100644 index 00000000..f798c006 --- /dev/null +++ b/packages/build/src/target/ModuleBuilder.ts @@ -0,0 +1,108 @@ + +import type { FileManager } from '@jitar/sourcing'; + +import type { Application, Module, Segmentation, Segment, SegmentImplementation as Implementation } from '../source'; +import { FileHelper } from '../utils'; + +import RemoteModuleBuilder from './RemoteModuleBuilder'; +import LocalModuleBuilder from './LocalModuleBuilder'; + +export default class ModuleBuilder +{ + #fileManager: FileManager; + + #localModuleBuilder = new LocalModuleBuilder(); + #remoteModuleBuilder = new RemoteModuleBuilder(); + #fileHelper = new FileHelper(); + + constructor(fileManager: FileManager) + { + this.#fileManager = fileManager; + } + + async build(application: Application): Promise + { + const repository = application.repository; + const segmentation = application.segmentation; + + const builds = repository.modules.map(module => this.#buildModule(module, segmentation)); + + await Promise.all(builds); + } + + async #buildModule(module: Module, segmentation: Segmentation): Promise + { + const moduleSegments = segmentation.getSegments(module.filename); + + // If the module is not part of any segment, it is an application module + + if (moduleSegments.length === 0) + { + await this.#buildSharedModule(module, segmentation); + } + else + { + // Otherwise, it is a segment module that can be called remotely + + const segmentBuilds = moduleSegments.map(segment => this.#buildSegmentModule(module, segment, segmentation)); + + const firstModuleSegment = moduleSegments[0]; + const segmentModule = firstModuleSegment.getModule(module.filename); + + const remoteBuild= segmentModule!.hasImplementations() + ? this.#buildRemoteModule(module, moduleSegments) + : []; + + await Promise.all([...segmentBuilds, remoteBuild]); + } + + this.#fileManager.delete(module.filename); + } + + async #buildSharedModule(module: Module, segmentation: Segmentation): Promise + { + const filename = this.#fileHelper.addSubExtension(module.filename, 'shared'); + const code = this.#localModuleBuilder.build(module, segmentation); + + return this.#fileManager.write(filename, code); + } + + async #buildSegmentModule(module: Module, segment: Segment, segmentation: Segmentation): Promise + { + const filename = this.#fileHelper.addSubExtension(module.filename, segment.name); + const code = this.#localModuleBuilder.build(module, segmentation, segment); + + return this.#fileManager.write(filename, code); + } + + async #buildRemoteModule(module: Module, segments: Segment[]): Promise + { + // The remote module contains calls to segmented procedures only + + const implementations = this.#getImplementations(module, segments); + const filename = this.#fileHelper.addSubExtension(module.filename, 'remote'); + const code = this.#remoteModuleBuilder.build(implementations); + + return this.#fileManager.write(filename, code); + } + + #getImplementations(module: Module, segments: Segment[]): Implementation[] + { + const segmentModules = segments.map(segment => segment.getModule(module.filename)); + const implementations = segmentModules.flatMap(segmentModule => segmentModule!.getImplementations()); + + // Implementation can be duplicated across segments + // We need to ensure that each implementation is unique + + const unique: Map = new Map(); + + for (const implementation of implementations) + { + const key = `${implementation.fqn}:${implementation.version.toString()}`; + + unique.set(key, implementation); + } + + return [...unique.values()]; + } +} diff --git a/packages/caching/src/building/utils/RemoteBuilder.ts b/packages/build/src/target/RemoteModuleBuilder.ts similarity index 52% rename from packages/caching/src/building/utils/RemoteBuilder.ts rename to packages/build/src/target/RemoteModuleBuilder.ts index 60e1035f..6c3f72dd 100644 --- a/packages/caching/src/building/utils/RemoteBuilder.ts +++ b/packages/build/src/target/RemoteModuleBuilder.ts @@ -1,75 +1,70 @@ -import { ReflectionDestructuredArray, ReflectionDestructuredObject, ReflectionDestructuredValue, ReflectionField, ReflectionParameter } from '@jitar/reflection'; -import { AccessLevels } from '@jitar/runtime'; +import { ESDestructuredArray, ESDestructuredObject, ESDestructuredValue, ESField } from '@jitar/analysis'; +import type { ESParameter } from '@jitar/analysis'; +import { AccessLevels } from '@jitar/execution'; -import Keyword from '../definitions/Keyword.js'; +import type { SegmentImplementation as Implementation } from '../source'; -import SegmentImplementation from '../models/SegmentImplementation.js'; -import SegmentModule from '../models/SegmentModule.js'; - -export default class RemoteBuilder +export default class RemoteModuleBuilder { - build(module: SegmentModule): string + build(implementations: Implementation[]): string { let code = ''; - for (const procedure of module.procedures) + for (const implementation of implementations) { - for (const implementation of procedure.implementations) - { - const asDefault = implementation.importKey === Keyword.DEFAULT; - - code += implementation.access === AccessLevels.PRIVATE - ? this.#createPrivateCode(procedure.fqn, implementation, asDefault) - : this.#createPublicCode(procedure.fqn, implementation, asDefault); - } + code += implementation.access === AccessLevels.PRIVATE + ? this.#createPrivateCode(implementation) + : this.#createPublicCode(implementation); } return code.trim(); } - #createPrivateCode(fqn: string, implementation: SegmentImplementation, asDefault: boolean): string + #createPrivateCode(implementation: Implementation): string { // Private procedures are not accessible from the outside. // Therefore we need to throw an error when they are called. + const fqn = implementation.fqn; const version = implementation.version; - const declaration = this.#createDeclaration(implementation, asDefault); + const declaration = this.#createDeclaration(implementation); const body = `throw new ProcedureNotAccessible('${fqn}', '${version}');`; return this.#createFunction(declaration, body); } - #createPublicCode(fqn: string, implementation: SegmentImplementation, asDefault: boolean): string + #createPublicCode(implementation: Implementation): string { // Public procedures are accessible from the outside. // Therefore we need to create a remote implementation. + const fqn = implementation.fqn; const version = implementation.version; - const args = this.#createArguments(implementation.executable.parameters); + const args = this.#createArguments(implementation.model.parameters); - const declaration = this.#createDeclaration(implementation, asDefault); + const declaration = this.#createDeclaration(implementation); const body = `return __run('${fqn}', '${version}', { ${args} }, this);`; return this.#createFunction(declaration, body); } - #createParameters(parameters: ReflectionParameter[]): string + #createParameters(parameters: ESParameter[]): string { const result: string[] = []; for (const parameter of parameters) { - if (parameter instanceof ReflectionField) + if (parameter instanceof ESField) { result.push(parameter.name); } - else if (parameter instanceof ReflectionDestructuredArray) + else if (parameter instanceof ESDestructuredArray) { result.push(parameter.toString()); } - else if (parameter instanceof ReflectionDestructuredObject) + else if (parameter instanceof ESDestructuredObject) { result.push(parameter.toString()); } @@ -78,26 +73,26 @@ export default class RemoteBuilder return result.join(', '); } - #createArguments(parameters: ReflectionParameter[]): string + #createArguments(parameters: ESParameter[]): string { const result = this.#extractArguments(parameters); return result.join(', '); } - #extractArguments(parameters: ReflectionParameter[]): string[] + #extractArguments(parameters: ESParameter[]): string[] { const result: string[] = []; for (const parameter of parameters) { - if (parameter instanceof ReflectionDestructuredValue) + if (parameter instanceof ESDestructuredValue) { const argumentz = this.#extractArguments(parameter.members); result.push(...argumentz); } - else if (parameter instanceof ReflectionField) + else if (parameter instanceof ESField) { const argument = this.#createNamedArgument(parameter); @@ -108,7 +103,7 @@ export default class RemoteBuilder return result; } - #createNamedArgument(parameter: ReflectionField): string + #createNamedArgument(parameter: ESField): string { const key = parameter.name; const value = key.startsWith('...') ? key.substring(3) : key; @@ -116,12 +111,12 @@ export default class RemoteBuilder return `'${key}': ${value}`; } - #createDeclaration(implementation: SegmentImplementation, asDefault: boolean): string + #createDeclaration(implementation: Implementation): string { - const name = implementation.executable.name; - const parameters = this.#createParameters(implementation.executable.parameters); + const name = implementation.model.name; + const parameters = this.#createParameters(implementation.model.parameters); - const prefix = asDefault ? `${Keyword.DEFAULT} ` : ''; + const prefix = implementation.importKey === 'default' ? 'default ' : ''; return `\nexport ${prefix}async function ${name}(${parameters})`; } diff --git a/packages/build/src/target/SegmentBuilder.ts b/packages/build/src/target/SegmentBuilder.ts new file mode 100644 index 00000000..e2c45b8e --- /dev/null +++ b/packages/build/src/target/SegmentBuilder.ts @@ -0,0 +1,186 @@ + +import { VersionParser } from '@jitar/execution'; +import type { FileManager } from '@jitar/sourcing'; +import { ESDestructuredArray, ESDestructuredObject } from '@jitar/analysis'; +import type { ESField, ESFunction, ESParameter } from '@jitar/analysis'; +import { Logger } from '@jitar/logging'; + +import type { Application, Segment, SegmentModule } from '../source'; +import { FileHelper } from '../utils'; + +const KEYWORD_DEFAULT = 'default'; +const RUNTIME_IMPORTS = 'import { Segment, Class, Procedure, Implementation, Version, NamedParameter, ArrayParameter, ObjectParameter } from "jitar";'; + +export default class SegmentBuilder +{ + #fileManager: FileManager; + + #logger = new Logger(); + #fileHelper = new FileHelper(); + #versionParser = new VersionParser(); + + constructor(fileManager: FileManager) + { + this.#fileManager = fileManager; + } + + async build(application: Application): Promise + { + const segmentation = application.segmentation; + + const builds = segmentation.segments.map(segment => this.#buildSegment(segment)); + + await Promise.all(builds); + } + + async #buildSegment(segment: Segment): Promise + { + const filename = `${segment.name}.segment.js`; + const code = this.#createCode(segment); + + await this.#fileManager.write(filename, code); + + this.#logger.info(`Built ${segment.name} segment (${segment.modules.length} modules, ${segment.procedures.length} procedures, ${segment.classes.length} classes)`); + } + + #createCode(segment: Segment): string + { + const importCode = this.#createImportCode(segment); + const segmentCode = this.#createSegmentCode(segment); + + return `${importCode}\n${segmentCode}`; + } + + #createImportCode(segment: Segment): string + { + const moduleImports = this.#createModuleImports(segment); + + return `${RUNTIME_IMPORTS}\n${moduleImports}`; + } + + #createModuleImports(segment: Segment): string + { + const imports = []; + + for (const module of segment.modules) + { + const filename = this.#fileHelper.addSubExtension(module.filename, segment.name); + const members = this.#createModuleImportMembers(module); + + const importRule = `import ${members} from "./${filename}";`; + + imports.push(importRule); + } + + return imports.join('\n'); + } + + #createModuleImportMembers(module: SegmentModule): string + { + const members = module.members; + + const defaultImplementation = members.find(member => member.importKey === KEYWORD_DEFAULT); + const hasDefaultImplementation = defaultImplementation !== undefined; + const defaultMemberImport = hasDefaultImplementation ? defaultImplementation.id : ''; + + const namedImplementations = members.filter(member => member.importKey !== KEYWORD_DEFAULT); + const nameImplementationImports = namedImplementations.map(member => `${member.importKey} as ${member.id}`); + const hasNamedImplementations = namedImplementations.length > 0; + const groupedNamedMemberImports = hasNamedImplementations ? `{ ${nameImplementationImports.join(', ')} }` : ''; + + const separator = hasDefaultImplementation && hasNamedImplementations ? ', ' : ''; + + return `${defaultMemberImport}${separator}${groupedNamedMemberImports}`; + } + + #createSegmentCode(segment: Segment): string + { + const lines: string[] = []; + + lines.push(`export default new Segment("${segment.name}")`); + + for (const clazz of segment.classes) + { + lines.push(`\t.addClass(new Class("${clazz.fqn}", ${clazz.id}))`); + } + + for (const procedure of segment.procedures) + { + lines.push(`\t.addProcedure(new Procedure("${procedure.fqn}")`); + + for (const implementation of procedure.implementations) + { + const version = this.#createVersionCode(implementation.version); + const parameters = this.#createParametersCode(implementation.model); + + lines.push(`\t\t.addImplementation(new Implementation(${version}, "${implementation.access}", ${parameters}, ${implementation.id}))`); + } + + lines.push('\t)'); + } + + return lines.join('\n'); + } + + #createVersionCode(versionString: string): string + { + const version = this.#versionParser.parse(versionString); + + return `new Version(${version.major}, ${version.minor}, ${version.patch})`; + } + + #createParametersCode(model: ESFunction): string + { + const result = this.#extractParameters(model.parameters); + + return `[${result.join(', ')}]`; + } + + #extractParameters(parameters: ESParameter[]): string[] + { + const result = []; + + // Named parameters are identified by their name. + // Destructured parameters are identified by their index. + + for (const parameter of parameters) + { + result.push(this.#extractParameter(parameter)); + } + + return result; + } + + #extractParameter(parameter: ESParameter): string + { + if (parameter instanceof ESDestructuredArray) + { + return this.#createArrayParameter(parameter); + } + else if (parameter instanceof ESDestructuredObject) + { + return this.#createObjectParameter(parameter); + } + + return this.#createNamedParameter(parameter); + } + + #createNamedParameter(parameter: ESField): string + { + return `new NamedParameter("${parameter.name}", ${parameter.value !== undefined})`; + } + + #createArrayParameter(parameter: ESDestructuredArray): string + { + const members = this.#extractParameters(parameter.members); + + return `new ArrayParameter([${members.join(', ')}])`; + } + + #createObjectParameter(parameter: ESDestructuredObject): string + { + const members = this.#extractParameters(parameter.members); + + return `new ObjectParameter([${members.join(', ')}])`; + } +} diff --git a/packages/build/src/target/index.ts b/packages/build/src/target/index.ts new file mode 100644 index 00000000..e166993d --- /dev/null +++ b/packages/build/src/target/index.ts @@ -0,0 +1,2 @@ + +export { default as ApplicationBuilder } from './Builder'; diff --git a/packages/build/src/utils/FileHelper.ts b/packages/build/src/utils/FileHelper.ts new file mode 100644 index 00000000..a554a9f7 --- /dev/null +++ b/packages/build/src/utils/FileHelper.ts @@ -0,0 +1,81 @@ + +const DEFAULT_EXTENSION = 'js'; +const EXTENSION_PATTERN = /\.js$/; + +export default class FileHelper +{ + translatePath(filename: string) + { + const parts = filename.split('/'); + const translated = []; + + for (let index = 0; index < parts.length; index++) + { + const part = parts[index].trim(); + + switch (part) + { + case '': continue; + case '.': continue; + case '..': translated.pop(); continue; + } + + translated.push(part); + } + + return translated.join('/'); + } + + makePathRelative(absoluteFilename: string, relativeToPath: string): string + { + if (relativeToPath === '') + { + return `./${absoluteFilename}`; + } + + const absoluteFilenameParts = absoluteFilename.split('/'); + const relativeToParts = relativeToPath.split('/'); + + while (absoluteFilenameParts[0] === relativeToParts[0]) + { + absoluteFilenameParts.shift(); + relativeToParts.shift(); + } + + const relativePath = relativeToParts.map(() => '..').join('/'); + + const prefix = relativeToParts.length > 0 ? relativePath : '.'; + const suffix = absoluteFilenameParts.join('/'); + + return `${prefix}/${suffix}`; + } + + makePathAbsolute(relativeFilename: string, relativeToPath: string): string + { + const fullPath = relativeToPath !== '' + ? `${relativeToPath}/${relativeFilename}` + : relativeFilename; + + return this.translatePath(fullPath); + } + + extractPath(filename: string) + { + return filename.split('/').slice(0, -1).join('/'); + } + + extractFilename(filename: string) + { + return filename.split('/').pop(); + } + + assureExtension(filename: string): string + { + return filename.endsWith(`.${DEFAULT_EXTENSION}`) ? filename : `${filename}.${DEFAULT_EXTENSION}`; + } + + addSubExtension(filename: string, subExtension: string): string + { + return filename.replace(EXTENSION_PATTERN, `.${subExtension}.${DEFAULT_EXTENSION}`); + } +} diff --git a/packages/caching/src/building/utils/IdGenerator.ts b/packages/build/src/utils/IdGenerator.ts similarity index 100% rename from packages/caching/src/building/utils/IdGenerator.ts rename to packages/build/src/utils/IdGenerator.ts diff --git a/packages/build/src/utils/index.ts b/packages/build/src/utils/index.ts new file mode 100644 index 00000000..b80860cb --- /dev/null +++ b/packages/build/src/utils/index.ts @@ -0,0 +1,3 @@ + +export { default as IdGenerator } from './IdGenerator'; +export { default as FileHelper } from './FileHelper'; diff --git a/packages/build/test/dummy.spec.ts b/packages/build/test/dummy.spec.ts new file mode 100644 index 00000000..2489f1ee --- /dev/null +++ b/packages/build/test/dummy.spec.ts @@ -0,0 +1,12 @@ + +import { describe, expect, it } from 'vitest'; + +describe('dummy', () => +{ + // TODO: Add real tests + + it('should not complain about missing tests', async () => + { + expect(true).toBeTruthy(); + }); +}); diff --git a/packages/reflection/tsconfig.json b/packages/build/tsconfig.json similarity index 100% rename from packages/reflection/tsconfig.json rename to packages/build/tsconfig.json diff --git a/packages/reflection/vite.config.ts b/packages/build/vite.config.ts similarity index 100% rename from packages/reflection/vite.config.ts rename to packages/build/vite.config.ts diff --git a/packages/caching/package.json b/packages/caching/package.json deleted file mode 100644 index c4232c71..00000000 --- a/packages/caching/package.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "name": "@jitar/caching", - "version": "0.7.5", - "description": "JavaScript caching library for the Jitar runtime.", - "author": "Masking Technology (https://jitar.dev)", - "license": "MIT", - "type": "module", - "types": "dist/lib.d.ts", - "exports": { - ".": "./dist/lib.js" - }, - "files": [ - "CHANGELOG.md", - "README.md", - "dist" - ], - "publishConfig": { - "access": "public" - }, - "scripts": { - "test": "vitest run", - "test-coverage": "vitest run --coverage", - "lint": "eslint . --ext .ts", - "build": "tsc -p tsconfig.json", - "clean": "rm -rf dist", - "prepublishOnly": "npm run clean && npm run build" - }, - "dependencies": { - "@jitar/reflection": "*", - "@jitar/runtime": "*" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/MaskingTechnology/jitar.git" - }, - "bugs": { - "url": "https://github.com/MaskingTechnology/jitar/issues" - }, - "homepage": "https://jitar.dev", - "keywords": [ - "javascript", - "cache", - "jitar" - ] -} diff --git a/packages/caching/src/CacheManager.ts b/packages/caching/src/CacheManager.ts deleted file mode 100644 index ff8214aa..00000000 --- a/packages/caching/src/CacheManager.ts +++ /dev/null @@ -1,37 +0,0 @@ - -import { FileManager, Files } from '@jitar/runtime'; - -import ApplicationCacheBuilder from './building/ApplicationCacheBuilder.js'; -import ApplicationCacheWriter from './building/ApplicationCacheWriter.js'; -import ApplicationReader from './building/ApplicationReader.js'; - -export default class CacheManager -{ - #projectFileManager: FileManager; - #appFileManager: FileManager; - - #reader: ApplicationReader; - #builder: ApplicationCacheBuilder; - #writer: ApplicationCacheWriter; - - constructor(projectFileManager: FileManager, appFileManager: FileManager) - { - this.#projectFileManager = projectFileManager; - this.#appFileManager = appFileManager; - - this.#reader = new ApplicationReader(appFileManager); - this.#builder = new ApplicationCacheBuilder(); - this.#writer = new ApplicationCacheWriter(appFileManager); - } - - async build(): Promise - { - const segmentFiles = await this.#projectFileManager.filter(Files.SEGMENT_PATTERN); - const moduleFiles = await this.#appFileManager.filter(Files.MODULE_PATTERN); - - const application = await this.#reader.read(segmentFiles, moduleFiles); - const cache = this.#builder.build(application); - - return this.#writer.write(cache); - } -} diff --git a/packages/caching/src/building/ApplicationCacheBuilder.ts b/packages/caching/src/building/ApplicationCacheBuilder.ts deleted file mode 100644 index af35246f..00000000 --- a/packages/caching/src/building/ApplicationCacheBuilder.ts +++ /dev/null @@ -1,38 +0,0 @@ - -import Application from './models/Application.js'; -import ApplicationCache from './models/ApplicationCache.js'; -import SegmentCache from './models/SegmentCache.js'; -import ModuleCache from './models/ModuleCache.js'; - -import ModuleCacheBuilder from './ModuleCacheBuilder.js'; -import SegmentCacheBuilder from './SegmentCacheBuilder.js'; - -export default class ApplicationCacheBuilder -{ - #moduleCacheBuilder: ModuleCacheBuilder; - #segmentCacheBuilder: SegmentCacheBuilder; - - constructor() - { - this.#moduleCacheBuilder = new ModuleCacheBuilder(); - this.#segmentCacheBuilder = new SegmentCacheBuilder(); - } - - build(application: Application): ApplicationCache - { - const segments = this.#buildSegmentCaches(application); - const modules = this.#buildModuleCaches(application); - - return new ApplicationCache(segments, modules); - } - - #buildSegmentCaches(application: Application): SegmentCache[] - { - return application.segments.map((segment) => this.#segmentCacheBuilder.build(segment)); - } - - #buildModuleCaches(application: Application): ModuleCache[] - { - return application.modules.map((module) => this.#moduleCacheBuilder.build(application, module)); - } -} diff --git a/packages/caching/src/building/ApplicationCacheWriter.ts b/packages/caching/src/building/ApplicationCacheWriter.ts deleted file mode 100644 index fcd9c08f..00000000 --- a/packages/caching/src/building/ApplicationCacheWriter.ts +++ /dev/null @@ -1,38 +0,0 @@ - -import { FileManager } from '@jitar/runtime'; - -import ApplicationCache from './models/ApplicationCache.js'; -import ModuleCache from './models/ModuleCache.js'; -import SegmentCache from './models/SegmentCache.js'; -import ModuleCacheWriter from './ModuleCacheWriter.js'; -import SegmentCacheWriter from './SegmentCacheWriter.js'; - -export default class ApplicationCacheWriter -{ - #moduleWriter: ModuleCacheWriter; - #segmentWriter: SegmentCacheWriter; - - constructor(fileManager: FileManager) - { - this.#moduleWriter = new ModuleCacheWriter(fileManager); - this.#segmentWriter = new SegmentCacheWriter(fileManager); - } - - async write(cache: ApplicationCache): Promise - { - return Promise.all([ - this.#writeSegmentCache(cache.segments), - this.#writeModuleCache(cache.modules) - ]).then(() => undefined); - } - - async #writeSegmentCache(segments: SegmentCache[]): Promise - { - return Promise.all(segments.map(async (segment) => this.#segmentWriter.write(segment))).then(() => undefined); - } - - async #writeModuleCache(modules: ModuleCache[]): Promise - { - return Promise.all(modules.map(async (module) => this.#moduleWriter.write(module))).then(() => undefined); - } -} diff --git a/packages/caching/src/building/ApplicationReader.ts b/packages/caching/src/building/ApplicationReader.ts deleted file mode 100644 index 6fef5e2a..00000000 --- a/packages/caching/src/building/ApplicationReader.ts +++ /dev/null @@ -1,39 +0,0 @@ - -import { FileManager } from '@jitar/runtime'; - -import Application from './models/Application.js'; -import Segment from './models/Segment.js'; -import Module from './models/Module.js'; - -import ModuleReader from './ModuleReader.js'; -import SegmentReader from './SegmentReader.js'; - -export default class ApplicationReader -{ - #moduleReader: ModuleReader; - #segmentReader: SegmentReader; - - constructor(fileManager: FileManager) - { - this.#moduleReader = new ModuleReader(fileManager); - this.#segmentReader = new SegmentReader(fileManager); - } - - async read(segmentFiles: string[], moduleFiles: string[]): Promise - { - return Promise.all([ - this.#readSegments(segmentFiles), - this.#readModules(moduleFiles) - ]).then(([segments, modules]) => new Application(segments, modules)); - } - - async #readSegments(segmentFiles: string[]): Promise - { - return Promise.all(segmentFiles.map(async (segmentFile) => this.#segmentReader.read(segmentFile))); - } - - async #readModules(moduleFiles: string[]): Promise - { - return Promise.all(moduleFiles.map(async (moduleFile) => this.#moduleReader.read(moduleFile))); - } -} diff --git a/packages/caching/src/building/ModuleCacheBuilder.ts b/packages/caching/src/building/ModuleCacheBuilder.ts deleted file mode 100644 index d13b1f62..00000000 --- a/packages/caching/src/building/ModuleCacheBuilder.ts +++ /dev/null @@ -1,14 +0,0 @@ - -import Application from './models/Application.js'; -import Module from './models/Module.js'; -import ModuleCache from './models/ModuleCache.js'; - -export default class ModuleCacheBuilder -{ - build(application: Application, module: Module): ModuleCache - { - const segment = application.getSegmentModule(module.filename); - - return new ModuleCache(module, segment); - } -} diff --git a/packages/caching/src/building/ModuleCacheWriter.ts b/packages/caching/src/building/ModuleCacheWriter.ts deleted file mode 100644 index e367abbb..00000000 --- a/packages/caching/src/building/ModuleCacheWriter.ts +++ /dev/null @@ -1,80 +0,0 @@ - -import { FileManager, convertToLocalFilename, convertToRemoteFilename } from '@jitar/runtime'; - -import ModuleCache from './models/ModuleCache.js'; -import Module from './models/Module.js'; -import SegmentModule from './models/SegmentModule.js'; - -import ImportRewriter from './utils/ImportRewriter.js'; -import RemoteBuilder from './utils/RemoteBuilder.js'; - -const importRewriter = new ImportRewriter(); -const remoteBuilder = new RemoteBuilder(); - -export default class ModuleCacheWriter -{ - #fileManager: FileManager; - - constructor(fileManager: FileManager) - { - this.#fileManager = fileManager; - } - - async write(cache: ModuleCache): Promise - { - return Promise.all([ - this.#writeLocal(cache), - this.#writeRemote(cache) - ]).then(() => undefined); - } - - async #writeLocal(cache: ModuleCache): Promise - { - // The local module files will be loaded in distributed mode. - // These require both the rewrite of imports and the addition of the source locations. - - const importCode = this.#rewriteAllImports(cache.module); - const sourceCode = this.#createSourceCode(cache.module); - - const filename = convertToLocalFilename(cache.module.filename); - const code = `${importCode}\n${sourceCode}`; - - return this.#fileManager.write(filename, code.trim()); - } - - #rewriteAllImports(module: Module): string - { - return importRewriter.rewrite(module.code, module.filename); - } - - #createSourceCode(module: Module): string - { - const filename = module.filename; - const classes = module.content.exportedClasses; - const classNames = classes.map(clazz => clazz.name); - const sourceCode = classNames.map(className => `${className}.source = "./${filename}";`); - - return sourceCode.join('\n'); - } - - async #writeRemote(cache: ModuleCache): Promise - { - // The remote module files will be loaded in distributed mode. - // These only have to call the global runProcedure(...) function. - - if (cache.segment === undefined) - { - return; - } - - const filename = convertToRemoteFilename(cache.module.filename); - const code = this.#createRemoteCode(cache.segment); - - return this.#fileManager.write(filename, code.trim()); - } - - #createRemoteCode(module: SegmentModule): string - { - return remoteBuilder.build(module); - } -} diff --git a/packages/caching/src/building/SegmentCacheBuilder.ts b/packages/caching/src/building/SegmentCacheBuilder.ts deleted file mode 100644 index 8fdbf81c..00000000 --- a/packages/caching/src/building/SegmentCacheBuilder.ts +++ /dev/null @@ -1,106 +0,0 @@ - -import DuplicateImplementation from './errors/DuplicateImplementation.js'; -import Segment from './models/Segment.js'; -import SegmentCache from './models/SegmentCache.js'; -import SegmentImplementation from './models/SegmentImplementation.js'; -import SegmentImport from './models/SegmentImport.js'; -import SegmentProcedure from './models/SegmentProcedure.js'; - -export default class SegmentCacheBuilder -{ - build(segment: Segment): SegmentCache - { - const files = this.#extractFiles(segment); - const imports = this.#createImports(segment); - const procedures = this.#mergeProcedures(segment); - - this.#validateProcedures(procedures); - - return new SegmentCache(segment.name, files, imports, procedures); - } - - #extractFiles(segment: Segment): string[] - { - return segment.modules.map(module => module.filename); - } - - #createImports(segment: Segment): SegmentImport[] - { - const imports = []; - - for (const module of segment.modules) - { - let members: string[] = []; - - for (const procedure of module.procedures) - { - const ids = procedure.implementations.map(implementation => this.#createImportMember(implementation)); - - members = [...members, ...ids]; - } - - imports.push(new SegmentImport(members, module.filename)); - } - - return imports; - } - - #createImportMember(implementation: SegmentImplementation): string - { - return `${implementation.importKey} : ${implementation.id}`; - } - - #mergeProcedures(segment: Segment): SegmentProcedure[] - { - const procedures = new Map(); - - for (const module of segment.modules) - { - for (const procedure of module.procedures) - { - if (procedures.has(procedure.fqn)) - { - const existingProcedure = procedures.get(procedure.fqn) as SegmentProcedure; - - for (const implementation of procedure.implementations) - { - existingProcedure.addImplementation(implementation); - } - - continue; - } - - const procedureCopy = new SegmentProcedure(procedure.fqn, [...procedure.implementations]); - - procedures.set(procedure.fqn, procedureCopy); - } - } - - return [...procedures.values()]; - } - - #validateProcedures(procedures: SegmentProcedure[]): void - { - for (const procedure of procedures) - { - this.#checkForDuplicateImplementations(procedure); - } - } - - #checkForDuplicateImplementations(procedure: SegmentProcedure): void - { - for (const implementation of procedure.implementations) - { - const duplicate = procedure.implementations.find(other => - { - return other.id !== implementation.id - && other.version === implementation.version; - }); - - if (duplicate !== undefined) - { - throw new DuplicateImplementation(procedure.fqn, implementation.version); - } - } - } -} diff --git a/packages/caching/src/building/SegmentCacheWriter.ts b/packages/caching/src/building/SegmentCacheWriter.ts deleted file mode 100644 index 724c0de1..00000000 --- a/packages/caching/src/building/SegmentCacheWriter.ts +++ /dev/null @@ -1,143 +0,0 @@ - -import { ReflectionDestructuredArray, ReflectionDestructuredObject, ReflectionField, ReflectionFunction, ReflectionParameter } from '@jitar/reflection'; -import { FileManager, VersionParser, createWorkerFilename, createRepositoryFilename } from '@jitar/runtime'; - -import SegmentCache from './models/SegmentCache.js'; -import SegmentImport from './models/SegmentImport.js'; -import SegmentProcedure from './models/SegmentProcedure.js'; - -export default class SegmentCacheWriter -{ - #fileManager: FileManager; - - constructor(fileManager: FileManager) - { - this.#fileManager = fileManager; - } - - async write(cache: SegmentCache): Promise - { - return Promise.all([ - this.#writeWorkerCache(cache), - this.#writeRepositoryCache(cache) - ]).then(() => undefined); - } - - async #writeWorkerCache(cache: SegmentCache): Promise - { - const importCode = this.#createImportCode(cache.imports); - const segmentCode = this.#createSegmentCode(cache.name, cache.procedures); - - const filename = createWorkerFilename(cache.name); - const code = `${importCode}\n${segmentCode}`; - - return this.#fileManager.write(filename, code); - } - - async #writeRepositoryCache(cache: SegmentCache): Promise - { - const filename = createRepositoryFilename(cache.name); - const code = `export const files = [\n\t"${[...cache.files].join('",\n\t"')}"\n];`; - - return this.#fileManager.write(filename, code); - } - - #createImportCode(imports: SegmentImport[]): string - { - const codes: string[] = []; - - for (const { members, from } of imports) - { - codes.push(`const { ${members.join(', ')} } = await __import("./${from}", "application", false);`); - } - - return codes.join('\n'); - } - - #createSegmentCode(name: string, procedures: SegmentProcedure[]): string - { - const codes: string[] = []; - - codes.push('const { Segment, Procedure, Implementation, Version, NamedParameter, ArrayParameter, ObjectParameter } = await __import("jitar", "runtime", false);'); - codes.push(`export const segment = new Segment("${name}")`); - - for (const procedure of procedures) - { - codes.push(`\t.addProcedure(new Procedure("${procedure.fqn}")`); - - for (const implementation of procedure.implementations) - { - const version = this.#createVersionCode(implementation.version); - const parameters = this.#createParametersCode(implementation.executable); - - codes.push(`\t\t.addImplementation(new Implementation(${version}, "${implementation.access}", ${parameters}, ${implementation.id}))`); - } - - codes.push('\t)'); - } - - return codes.join('\n'); - } - - #createVersionCode(versionString: string): string - { - const version = VersionParser.parse(versionString); - - return `new Version(${version.major}, ${version.minor}, ${version.patch})`; - } - - #createParametersCode(executable: ReflectionFunction): string - { - const result = this.#extractParameters(executable.parameters); - - return `[${result.join(', ')}]`; - } - - #extractParameters(parameters: ReflectionParameter[]): string[] - { - const result: string[] = []; - - // Named parameters are identified by their name. - // Destructured parameters are identified by their index. - - for (const parameter of parameters) - { - result.push(this.#extractParameter(parameter)); - } - - return result; - } - - #extractParameter(parameter: ReflectionParameter): string - { - if (parameter instanceof ReflectionDestructuredArray) - { - return this.#createArrayParameter(parameter); - } - else if (parameter instanceof ReflectionDestructuredObject) - { - return this.#createObjectParameter(parameter); - } - - return this.#createNamedParameter(parameter); - } - - #createNamedParameter(parameter: ReflectionField): string - { - return `new NamedParameter("${parameter.name}", ${parameter.value !== undefined})`; - } - - #createArrayParameter(parameter: ReflectionDestructuredArray): string - { - const members = this.#extractParameters(parameter.members); - - return `new ArrayParameter([${members.join(', ')}])`; - } - - #createObjectParameter(parameter: ReflectionDestructuredObject): string - { - const members = this.#extractParameters(parameter.members); - - return `new ObjectParameter([${members.join(', ')}])`; - } -} diff --git a/packages/caching/src/building/SegmentReader.ts b/packages/caching/src/building/SegmentReader.ts deleted file mode 100644 index 5cce87dd..00000000 --- a/packages/caching/src/building/SegmentReader.ts +++ /dev/null @@ -1,165 +0,0 @@ - -import { ReflectionFunction, ReflectionModule, Reflector } from '@jitar/reflection'; -import { FileManager } from '@jitar/runtime'; - -import FunctionNotAsync from './errors/FunctionNotAsync.js'; -import InvalidSegmentFilename from './errors/InvalidSegmentFilename.js'; -import MissingModuleExport from './errors/MissingModuleExport.js'; -import SegmentFileNotLoaded from './errors/SegmentFileNotLoaded.js'; -import SegmentModuleNotLoaded from './errors/SegmentModuleNotLoaded.js'; - -import Segment from './models/Segment.js'; -import SegmentImplementation from './models/SegmentImplementation.js'; -import SegmentModule from './models/SegmentModule.js'; -import SegmentProcedure from './models/SegmentProcedure.js'; - -import SegmentFile from './types/SegmentFile.js'; -import SegmentImports from './types/SegmentImports.js'; - -import IdGenerator from './utils/IdGenerator.js'; - -const SEGMENT_FILE_EXTENSION = '.segment.json'; -const DEFAULT_ACCESS_LEVEL = 'private'; -const DEFAULT_VERSION_NUMBER = '0.0.0'; - -const reflector = new Reflector(); - -export default class SegmentReader -{ - #fileManager: FileManager; - - constructor(fileManager: FileManager) - { - this.#fileManager = fileManager; - } - - async read(filename: string): Promise - { - const name = this.#extractSegmentName(filename); - const definition = await this.#loadSegmentDefinition(filename); - const modules = await this.#createSegmentModules(definition); - - return new Segment(name, modules); - } - - #extractSegmentName(filename: string): string - { - const file = filename.split('/').pop(); - - if (file === undefined || file === '') - { - throw new InvalidSegmentFilename(filename); - } - - return file.replace(SEGMENT_FILE_EXTENSION, ''); - } - - async #loadSegmentDefinition(filename: string): Promise - { - try - { - const content = await this.#fileManager.getContent(filename); - - return JSON.parse(content.toString()) as SegmentFile; - } - catch (error: unknown) - { - const message = error instanceof Error ? error.message : String(error); - - throw new SegmentFileNotLoaded(filename, message); - } - } - - async #createSegmentModules(definition: SegmentFile): Promise - { - const modules: SegmentModule[] = []; - const idGenerator = new IdGenerator(); - - for (const [filename, moduleImports] of Object.entries(definition)) - { - const fullFilename = filename.endsWith('.js') ? filename : `${filename}.js`; - const absoluteFilename = this.#fileManager.getAbsoluteLocation(fullFilename); - const module = await this.#createSegmentModule(absoluteFilename, moduleImports, idGenerator); - - modules.push(module); - } - - return modules; - } - - async #createSegmentModule(absoluteFilename: string, imports: SegmentImports, idGenerator: IdGenerator): Promise - { - const module = await this.#loadSegmentModule(absoluteFilename); - const relativeFilename = this.#fileManager.getRelativeLocation(absoluteFilename); - const moduleName = this.#extractModuleName(relativeFilename); - const procedures = this.#extractSegmentProcedures(module, moduleName, imports, idGenerator); - - return new SegmentModule(relativeFilename, procedures); - } - - async #loadSegmentModule(absoluteLocation: string): Promise - { - try - { - const code = await this.#fileManager.getContent(absoluteLocation); - - return reflector.parse(code.toString()); - } - catch (error: unknown) - { - const message = error instanceof Error ? error.message : String(error); - - throw new SegmentModuleNotLoaded(absoluteLocation, message); - } - } - - #extractModuleName(relativeFilename: string): string - { - const moduleParts = relativeFilename.split('/'); - moduleParts.pop(); - - return moduleParts.join('/'); - } - - #extractSegmentProcedures(module: ReflectionModule, moduleName: string, imports: SegmentImports, idGenerator: IdGenerator): SegmentProcedure[] - { - const procedures = new Map(); - - for (const [importKey, properties] of Object.entries(imports)) - { - const executable = module.getExported(importKey) as ReflectionFunction; - - if (executable === undefined) - { - throw new MissingModuleExport(moduleName, importKey); - } - else if ((executable instanceof ReflectionFunction) === false) - { - continue; - } - else if (executable.isAsync === false) - { - throw new FunctionNotAsync(moduleName, executable.name); - } - - const procedureName = properties.as ?? executable.name; - const access = properties.access ?? DEFAULT_ACCESS_LEVEL; - const version = properties.version ?? DEFAULT_VERSION_NUMBER; - - const id = idGenerator.next(); - const fqn = moduleName !== '' ? `${moduleName}/${procedureName}` : procedureName; - - const implementation = new SegmentImplementation(id, importKey, access, version, executable); - - const procedure = procedures.has(fqn) - ? procedures.get(fqn) as SegmentProcedure - : new SegmentProcedure(fqn); - - procedure.addImplementation(implementation); - - procedures.set(fqn, procedure); - } - - return [...procedures.values()]; - } -} diff --git a/packages/caching/src/building/definitions/Keyword.ts b/packages/caching/src/building/definitions/Keyword.ts deleted file mode 100644 index d201d3bd..00000000 --- a/packages/caching/src/building/definitions/Keyword.ts +++ /dev/null @@ -1,9 +0,0 @@ - -const Keyword = -{ - DEFAULT: 'default' -}; - -Object.freeze(Keyword); - -export default Keyword; diff --git a/packages/caching/src/building/errors/SegmentModuleNotLoaded.ts b/packages/caching/src/building/errors/SegmentModuleNotLoaded.ts deleted file mode 100644 index 93688096..00000000 --- a/packages/caching/src/building/errors/SegmentModuleNotLoaded.ts +++ /dev/null @@ -1,8 +0,0 @@ - -export default class SegmentModuleNotLoaded extends Error -{ - constructor(filename: string, message: string) - { - super(`Segment module could not be loaded from '${filename}' because of: ${message}`); - } -} diff --git a/packages/caching/src/building/models/Application.ts b/packages/caching/src/building/models/Application.ts deleted file mode 100644 index a5209b84..00000000 --- a/packages/caching/src/building/models/Application.ts +++ /dev/null @@ -1,32 +0,0 @@ - -import Module from './Module.js'; -import Segment from './Segment.js'; -import SegmentModule from './SegmentModule.js'; - -export default class Application -{ - #segments: Segment[]; - #modules: Module[]; - - constructor(segments: Segment[], modules: Module[]) - { - this.#segments = segments; - this.#modules = modules; - } - - get segments() { return this.#segments; } - - get modules() { return this.#modules; } - - getSegmentModule(filename: string): SegmentModule | undefined - { - const segment = this.#segments.find((segment) => segment.hasModule(filename)); - - if (segment === undefined) - { - return undefined; - } - - return segment.getModule(filename); - } -} diff --git a/packages/caching/src/building/models/ApplicationCache.ts b/packages/caching/src/building/models/ApplicationCache.ts deleted file mode 100644 index a5341d4d..00000000 --- a/packages/caching/src/building/models/ApplicationCache.ts +++ /dev/null @@ -1,19 +0,0 @@ - -import ModuleCache from './ModuleCache.js'; -import SegmentCache from './SegmentCache.js'; - -export default class ApplicationCache -{ - #segments: SegmentCache[]; - #modules: ModuleCache[]; - - constructor(segments: SegmentCache[], modules: ModuleCache[]) - { - this.#segments = segments; - this.#modules = modules; - } - - get segments() { return this.#segments; } - - get modules() { return this.#modules; } -} diff --git a/packages/caching/src/building/models/ModuleCache.ts b/packages/caching/src/building/models/ModuleCache.ts deleted file mode 100644 index e1120c0f..00000000 --- a/packages/caching/src/building/models/ModuleCache.ts +++ /dev/null @@ -1,19 +0,0 @@ - -import Module from './Module.js'; -import SegmentModule from './SegmentModule.js'; - -export default class ModuleCache -{ - #module: Module; - #segment?: SegmentModule; - - constructor(module: Module, segment?: SegmentModule) - { - this.#module = module; - this.#segment = segment; - } - - get module() { return this.#module; } - - get segment() { return this.#segment; } -} diff --git a/packages/caching/src/building/models/Segment.ts b/packages/caching/src/building/models/Segment.ts deleted file mode 100644 index aba5183a..00000000 --- a/packages/caching/src/building/models/Segment.ts +++ /dev/null @@ -1,28 +0,0 @@ - -import SegmentModule from './SegmentModule.js'; - -export default class Segment -{ - #name: string; - #modules: SegmentModule[]; - - constructor(name: string, modules: SegmentModule[]) - { - this.#name = name; - this.#modules = modules; - } - - get name() { return this.#name; } - - get modules() { return this.#modules; } - - hasModule(filename: string): boolean - { - return this.#modules.some((module) => module.filename === filename); - } - - getModule(filename: string): SegmentModule | undefined - { - return this.#modules.find((module) => module.filename === filename); - } -} diff --git a/packages/caching/src/building/models/SegmentCache.ts b/packages/caching/src/building/models/SegmentCache.ts deleted file mode 100644 index 71624c5d..00000000 --- a/packages/caching/src/building/models/SegmentCache.ts +++ /dev/null @@ -1,27 +0,0 @@ - -import SegmentImport from './SegmentImport.js'; -import SegmentProcedure from './SegmentProcedure.js'; - -export default class SegmentCache -{ - #name: string; - #imports: SegmentImport[]; - #procedures: SegmentProcedure[]; - #files: string[]; - - constructor(name: string, files: string[], imports: SegmentImport[], procedures: SegmentProcedure[]) - { - this.#name = name; - this.#files = files; - this.#imports = imports; - this.#procedures = procedures; - } - - get name() { return this.#name; } - - get files() { return this.#files; } - - get imports() { return this.#imports; } - - get procedures() { return this.#procedures; } -} diff --git a/packages/caching/src/building/models/SegmentImplementation.ts b/packages/caching/src/building/models/SegmentImplementation.ts deleted file mode 100644 index 664d773c..00000000 --- a/packages/caching/src/building/models/SegmentImplementation.ts +++ /dev/null @@ -1,30 +0,0 @@ - -import { ReflectionFunction } from '@jitar/reflection'; - -export default class SegmentImplementation -{ - #id: string; - #importKey: string; - #access: string; - #version: string; - #executable: ReflectionFunction; - - constructor(id: string, importKey: string, access: string, version: string, executable: ReflectionFunction) - { - this.#id = id; - this.#importKey = importKey; - this.#access = access; - this.#version = version; - this.#executable = executable; - } - - get id() { return this.#id; } - - get importKey() { return this.#importKey; } - - get access() { return this.#access; } - - get version() { return this.#version; } - - get executable() { return this.#executable; } -} diff --git a/packages/caching/src/building/models/SegmentModule.ts b/packages/caching/src/building/models/SegmentModule.ts deleted file mode 100644 index f4a7ff9c..00000000 --- a/packages/caching/src/building/models/SegmentModule.ts +++ /dev/null @@ -1,18 +0,0 @@ - -import SegmentProcedure from './SegmentProcedure.js'; - -export default class SegmentModule -{ - #filename: string; - #procedures: SegmentProcedure[] = []; - - constructor(filename: string, procedures: SegmentProcedure[]) - { - this.#filename = filename; - this.#procedures = procedures; - } - - get filename() { return this.#filename; } - - get procedures() { return this.#procedures; } -} diff --git a/packages/caching/src/building/models/SegmentProcedure.ts b/packages/caching/src/building/models/SegmentProcedure.ts deleted file mode 100644 index cf07e84c..00000000 --- a/packages/caching/src/building/models/SegmentProcedure.ts +++ /dev/null @@ -1,23 +0,0 @@ - -import SegmentImplementation from './SegmentImplementation.js'; - -export default class SegmentProcedure -{ - #fqn: string; - #implementations: SegmentImplementation[] = []; - - constructor(fqn: string, implementations: SegmentImplementation[] = []) - { - this.#fqn = fqn; - this.#implementations = implementations; - } - - get fqn() { return this.#fqn; } - - get implementations() { return this.#implementations; } - - addImplementation(implementation: SegmentImplementation): void - { - this.#implementations.push(implementation); - } -} diff --git a/packages/caching/src/building/types/SegmentFile.ts b/packages/caching/src/building/types/SegmentFile.ts deleted file mode 100644 index 5eadb62f..00000000 --- a/packages/caching/src/building/types/SegmentFile.ts +++ /dev/null @@ -1,6 +0,0 @@ - -import SegmentImports from './SegmentImports.js'; - -type SegmentFile = Record; - -export default SegmentFile; diff --git a/packages/caching/src/building/types/SegmentImportProperties.ts b/packages/caching/src/building/types/SegmentImportProperties.ts deleted file mode 100644 index 1e307892..00000000 --- a/packages/caching/src/building/types/SegmentImportProperties.ts +++ /dev/null @@ -1,9 +0,0 @@ - -type SegmentImportProperties = -{ - as?: string; - access?: string; - version?: string; -} - -export default SegmentImportProperties; diff --git a/packages/caching/src/building/types/SegmentImports.ts b/packages/caching/src/building/types/SegmentImports.ts deleted file mode 100644 index f4c042e5..00000000 --- a/packages/caching/src/building/types/SegmentImports.ts +++ /dev/null @@ -1,6 +0,0 @@ - -import SegmentImportProperties from './SegmentImportProperties.js'; - -type SegmentImports = Record; - -export default SegmentImports; diff --git a/packages/caching/src/building/utils/ImportRewriter.ts b/packages/caching/src/building/utils/ImportRewriter.ts deleted file mode 100644 index b4af95d2..00000000 --- a/packages/caching/src/building/utils/ImportRewriter.ts +++ /dev/null @@ -1,157 +0,0 @@ - -import { ReflectionImport, Reflector } from '@jitar/reflection'; - -import Keyword from '../definitions/Keyword.js'; - -const IMPORT_PATTERN = /import\s(?:["'\s]*([\w*{}\n, ]+)from\s*)?["'\s]*([@\w/._-]+)["'\s].*/g; -const APPLICATION_MODULE_INDICATORS = ['.', '/', 'http:', 'https:']; - -const reflector = new Reflector(); - -export default class ImportRewriter -{ - rewrite(code: string, filename: string): string - { - const replacer = (statement: string) => this.#replaceImport(statement, filename); - - return code.replaceAll(IMPORT_PATTERN, replacer); - } - - #replaceImport(statement: string, filename: string): string - { - const dependency = reflector.parseImport(statement); - - return this.#isApplicationModule(dependency) - ? this.#rewriteApplicationImport(dependency, filename) - : this.#rewriteRuntimeImport(dependency, filename); - } - - #isApplicationModule(dependency: ReflectionImport): boolean - { - return APPLICATION_MODULE_INDICATORS.some(indicator => dependency.from.startsWith(indicator, 1)); - } - - #rewriteApplicationImport(dependency: ReflectionImport, filename: string): string - { - return this.#rewriteImport(dependency, 'application', filename); - } - - #rewriteRuntimeImport(dependency: ReflectionImport, filename: string): string - { - return this.#rewriteImport(dependency, 'runtime', filename); - } - - #rewriteImport(dependency: ReflectionImport, scope: string, filename: string): string - { - const from = this.#rewriteImportFrom(dependency, filename); - - if (dependency.members.length === 0) - { - return `await __import("${from}", "${scope}");`; - } - - const members = this.#rewriteImportMembers(dependency); - const extractDefault = this.#mustUseAs(dependency) ? 'true' : 'false'; - - return `const ${members} = await __import("${from}", "${scope}", ${extractDefault});`; - } - - #rewriteImportFrom(dependency: ReflectionImport, filename: string): string - { - const from = dependency.from.substring(1, dependency.from.length - 1); - - return this.#isApplicationModule(dependency) - ? this.#mergeFilenames(filename, from) - : from; - } - - #rewriteImportMembers(dependency: ReflectionImport): string - { - if (this.#mustUseAs(dependency)) - { - return dependency.members[0].as; - } - - const members = dependency.members.map(member => member.name !== member.as ? `${member.name} : ${member.as}` : member.name); - - return `{ ${members.join(', ')} }`; - } - - #mergeFilenames(sourceFilename: string, importFilename: string): string - { - const sourcePath = this.#extractFilepath(sourceFilename); - - const concatenated = `${sourcePath}/${importFilename}`; - const translated = this.#translateFilename(concatenated); - const rooted = this.#ensureRoot(translated); - - return this.#ensureExtension(rooted); - } - - #extractFilepath(filename: string) - { - return filename.split('/').slice(0, -1).join('/'); - } - - #translateFilename(filename: string) - { - const parts = filename.split('/'); - const translated = []; - - translated.push(parts[0]); - - for (let index = 1; index < parts.length; index++) - { - const part = parts[index].trim(); - - switch (part) - { - case '': continue; - case '.': continue; - case '..': translated.pop(); continue; - } - - translated.push(part); - } - - return translated.join('/'); - } - - #ensureRoot(filename: string): string - { - if (filename.startsWith('./')) - { - return filename; - } - - if (filename.startsWith('//')) - { - filename = filename.substring(1); - } - - return filename.startsWith('/') ? `.${filename}` : `./${filename}`; - } - - #ensureExtension(filename: string): string - { - return filename.endsWith('.js') ? filename : `${filename}.js`; - } - - #mustUseAs(dependency: ReflectionImport): boolean - { - return this.#doesImportAll(dependency) - || this.#doesImportDefault(dependency); - } - - #doesImportAll(dependency: ReflectionImport): boolean - { - return dependency.members.length === 1 - && dependency.members[0].name === '*'; - } - - #doesImportDefault(dependency: ReflectionImport): boolean - { - return dependency.members.length === 1 - && dependency.members[0].name === Keyword.DEFAULT; - } -} diff --git a/packages/caching/src/lib.ts b/packages/caching/src/lib.ts deleted file mode 100644 index ceacc9bb..00000000 --- a/packages/caching/src/lib.ts +++ /dev/null @@ -1,2 +0,0 @@ - -export { default as CacheManager } from './CacheManager.js'; diff --git a/packages/caching/test/CacheManager.spec.ts b/packages/caching/test/CacheManager.spec.ts deleted file mode 100644 index 10464e6c..00000000 --- a/packages/caching/test/CacheManager.spec.ts +++ /dev/null @@ -1,26 +0,0 @@ - -import { describe, expect, it } from 'vitest'; - -import CacheManager from '../src/CacheManager'; - -import { TestFileManager } from './_fixtures/TestFileManager.fixture'; - -describe('CacheManager', () => -{ - describe('.build()', () => - { - it('should write a segment from a definition file', async () => - { - // We need to create a new file manager for each test, because the file manager - // keeps track of the files that are written to disk. - - const fileManager = new TestFileManager(); - const cacheManager = new CacheManager(fileManager, fileManager); - - await cacheManager.build(); - - const result = fileManager.writtenFiles; - expect(result.size).toBe(14); - }); - }); -}); diff --git a/packages/caching/test/_fixtures/CacheFiles.fixture.ts b/packages/caching/test/_fixtures/CacheFiles.fixture.ts deleted file mode 100644 index 6598edc4..00000000 --- a/packages/caching/test/_fixtures/CacheFiles.fixture.ts +++ /dev/null @@ -1,190 +0,0 @@ - -import { CONSTANTS } from '../_fixtures/Constants.fixture'; - -const ORDER_SEGMENT_REPOSITORY = `export const files = [ -\t"order/createOrder.js", -\t"order/storeOrder.js" -];`; - -const ORDER_SEGMENT_WORKER = -`const { default : $1 } = await __import("./order/createOrder.js", "application", false); -const { v0_0_0 : $2, v1_0_0 : $3 } = await __import("./order/storeOrder.js", "application", false); -const { Segment, Procedure, Implementation, Version, NamedParameter, ArrayParameter, ObjectParameter } = await __import("jitar", "runtime", false); -export const segment = new Segment("order") -\t.addProcedure(new Procedure("order/createOrder") -\t\t.addImplementation(new Implementation(new Version(0, 0, 0), "private", [new NamedParameter("items", false)], $1)) -\t) -\t.addProcedure(new Procedure("order/storeOrder") -\t\t.addImplementation(new Implementation(new Version(0, 0, 0), "public", [new NamedParameter("order", false)], $2)) -\t\t.addImplementation(new Implementation(new Version(1, 0, 0), "public", [new NamedParameter("...orders", false)], $3)) -\t)`; - -const PRODUCT_SEGMENT_REPOSITORY = `export const files = [ -\t"product/getProducts.js" -];`; - -const PRODUCT_SEGMENT_WORKER = -`const { default : $1, searchProducts : $2 } = await __import("./product/getProducts.js", "application", false); -const { default : $3, searchProducts : $4 } = await __import("./product/getProducts_v1.js", "application", false); -const { Segment, Procedure, Implementation, Version, NamedParameter, ArrayParameter, ObjectParameter } = await __import("jitar", "runtime", false); -export const segment = new Segment("product") -\t.addProcedure(new Procedure("product/getProducts") -\t\t.addImplementation(new Implementation(new Version(0, 0, 0), "private", [new NamedParameter("id", false)], $1)) -\t\t.addImplementation(new Implementation(new Version(1, 0, 0), "private", [new NamedParameter("id", false)], $3)) -\t) -\t.addProcedure(new Procedure("product/searchProducts") -\t\t.addImplementation(new Implementation(new Version(0, 0, 0), "public", [new ObjectParameter([new NamedParameter("query", false), new NamedParameter("sort", false)])], $2)) -\t\t.addImplementation(new Implementation(new Version(1, 0, 0), "public", [new ArrayParameter([new NamedParameter("query", false), new NamedParameter("sort", false)])], $4)) -\t)`; - -const CREATE_ORDER_LOCAL = -`const { Order, OrderLine } = await __import("./order/models.js", "application", false); - -export default async function createOrder(items) -{ - return 'order' -} - -export async function createOrderLine(item) -{ - return 'order line'; -}`; - -const CREATE_ORDER_REMOTE = -`export default async function createOrder(items) { -\tthrow new ProcedureNotAccessible('${CONSTANTS.CREATE_ORDER_FQN}', '0.0.0'); -}`; - -const STORE_ORDER_LOCAL = -`const mysql = await __import("mysql", "runtime", true); -const createId = await __import("uuid", "runtime", true); -const { Order } = await __import("./order/models.js", "application", false); - -export async function v0_0_0(order) -{ - return 'order v0' -} - -export async function v1_0_0(...orders) -{ - return 'order v1'; -}`; - -const STORE_ORDER_REMOTE = -`export async function v0_0_0(order) { -\treturn __run('${CONSTANTS.STORE_ORDER_FQN}', '0.0.0', { 'order': order }, this); -} - -export async function v1_0_0(...orders) { -\treturn __run('${CONSTANTS.STORE_ORDER_FQN}', '1.0.0', { '...orders': orders }, this); -}`; - -const ORDER_MODELS_LOCAL = -`export class Order {} -export class OrderLine {} - -Order.source = "./order/models.js"; -OrderLine.source = "./order/models.js";`; - -const GET_PRODUCTS_LOCAL = -`const mongodb = await __import("mongodb", "runtime", true); -const { Product } = await __import("./product/models.js", "application", false); - -export default async function getProducts(id) -{ - return 'product'; -} - -function validateFound(product) -{ - /* ... */ -} - -export async function searchProducts({query, sort}) -{ - return 'product list'; -}`; - -const GET_PRODUCTS_REMOTE = -`export default async function getProducts(id) { -\tthrow new ProcedureNotAccessible('${CONSTANTS.GET_PRODUCTS_FQN}', '0.0.0'); -} - -export async function searchProducts({ query , sort }) { -\treturn __run('${CONSTANTS.SEARCH_PRODUCTS_FQN}', '0.0.0', { 'query': query, 'sort': sort }, this); -}`; - -const GET_PRODUCTS_LOCAL_V1 = -`const mongodb = await __import("mongodb", "runtime", true); -const { Product } = await __import("./product/models.js", "application", false); - -export default async function getProducts(id) -{ - return 'product'; -} - -function validateFound(product) -{ - /* ... */ -} - -export async function searchProducts([query, sort]) -{ - return 'product list'; -}`; - -const GET_PRODUCTS_REMOTE_V1 = -`export default async function getProducts(id) { -\tthrow new ProcedureNotAccessible('${CONSTANTS.GET_PRODUCTS_FQN}', '1.0.0'); -} - -export async function searchProducts([ query , sort ]) { -\treturn __run('${CONSTANTS.SEARCH_PRODUCTS_FQN}', '1.0.0', { 'query': query, 'sort': sort }, this); -}`; - -const PRODUCT_MODELS_LOCAL = -`export class Product {} - -Product.source = "./product/models.js";`; - -const CACHE_FILES = -{ - './order.segment.worker.js': ORDER_SEGMENT_WORKER, - './order.segment.repository.js': ORDER_SEGMENT_REPOSITORY, - './product.segment.worker.js': PRODUCT_SEGMENT_WORKER, - './product.segment.repository.js': PRODUCT_SEGMENT_REPOSITORY, - './order/createOrder.local.js': CREATE_ORDER_LOCAL, - './order/createOrder.remote.js': CREATE_ORDER_REMOTE, - './order/storeOrder.local.js': STORE_ORDER_LOCAL, - './order/storeOrder.remote.js': STORE_ORDER_REMOTE, - './order/models.local.js': ORDER_MODELS_LOCAL, - './product/getProducts.local.js': GET_PRODUCTS_LOCAL, - './product/getProducts.remote.js': GET_PRODUCTS_REMOTE, - './product/getProducts_v1.local.js': GET_PRODUCTS_LOCAL_V1, - './product/getProducts_v1.remote.js': GET_PRODUCTS_REMOTE_V1, - './product/models.local.js': PRODUCT_MODELS_LOCAL -}; - -const CACHE_SEGMENT_FILENAMES = -[ - './order.segment.worker.js', - './order.segment.repository.js', - './product.segment.worker.js', - './product.segment.repository.js', -]; - -const CACHE_MODULE_FILENAMES = -[ - './order/createOrder.local.js', - './order/createOrder.remote.js', - './order/storeOrder.local.js', - './order/storeOrder.remote.js', - './order/models.local.js', - './product/getProducts.local.js', - './product/getProducts.remote.js', - './product/getProducts_v1.local.js', - './product/getProducts_v1.remote.js', - './product/models.local.js', -]; - -export { CACHE_FILES, CACHE_SEGMENT_FILENAMES, CACHE_MODULE_FILENAMES }; diff --git a/packages/caching/test/_fixtures/Constants.fixture.ts b/packages/caching/test/_fixtures/Constants.fixture.ts deleted file mode 100644 index 86d2acd0..00000000 --- a/packages/caching/test/_fixtures/Constants.fixture.ts +++ /dev/null @@ -1,9 +0,0 @@ - -const CONSTANTS = { - CREATE_ORDER_FQN: 'order/createOrder', - STORE_ORDER_FQN: 'order/storeOrder', - GET_PRODUCTS_FQN: 'product/getProducts', - SEARCH_PRODUCTS_FQN: 'product/searchProducts' -}; - -export { CONSTANTS }; diff --git a/packages/caching/test/_fixtures/SourceFiles.fixture.ts b/packages/caching/test/_fixtures/SourceFiles.fixture.ts deleted file mode 100644 index 38af4332..00000000 --- a/packages/caching/test/_fixtures/SourceFiles.fixture.ts +++ /dev/null @@ -1,140 +0,0 @@ - -// With .js extension -const ORDER_SEGMENT = ` -{ - "./order/createOrder.js": - { - "default": {} - }, - "./order/storeOrder.js": - { - "v0_0_0": { "access": "public", "as": "storeOrder" }, - "v1_0_0": { "access": "public", "as": "storeOrder", "version": "1.0.0" } - } -} -`; - -// Without .js extension -const PRODUCT_SEGMENT = ` -{ - "./product/getProducts": - { - "default": { "access": "private" }, - "searchProducts": { "access": "public" } - }, - "./product/getProducts_v1": - { - "default": { "access": "private", "version": "1.0.0" }, - "searchProducts": { "access": "public", "version": "1.0.0" } - } -} -`; - -const CREATE_ORDER = ` -import { Order, OrderLine } from './models'; - -export default async function createOrder(items) -{ - return 'order' -} - -export async function createOrderLine(item) -{ - return 'order line'; -} -`; - -const STORE_ORDER = ` -import mysql from 'mysql'; -import createId from 'uuid'; -import { Order } from './models'; - -export async function v0_0_0(order) -{ - return 'order v0' -} - -export async function v1_0_0(...orders) -{ - return 'order v1'; -} -`; - -const ORDER_MODELS = ` -export class Order {} -export class OrderLine {} -`; - -const GET_PRODUCTS = ` -import * as mongodb from 'mongodb'; -import { Product } from './models'; - -export default async function getProducts(id) -{ - return 'product'; -} - -function validateFound(product) -{ - /* ... */ -} - -export async function searchProducts({query, sort}) -{ - return 'product list'; -} -`; - -const GET_PRODUCTS_V1 = ` -import * as mongodb from 'mongodb'; -import { Product } from './models'; - -export default async function getProducts(id) -{ - return 'product'; -} - -function validateFound(product) -{ - /* ... */ -} - -export async function searchProducts([query, sort]) -{ - return 'product list'; -} -`; - -const PRODUCT_MODELS = ` -export class Product {} -`; - -const SOURCE_FILES = -{ - './order.segment.json': ORDER_SEGMENT, - './product.segment.json': PRODUCT_SEGMENT, - './order/createOrder.js': CREATE_ORDER, - './order/storeOrder.js': STORE_ORDER, - './order/models.js': ORDER_MODELS, - './product/getProducts.js': GET_PRODUCTS, - './product/getProducts_v1.js': GET_PRODUCTS_V1, - './product/models.js': PRODUCT_MODELS -}; - -const SOURCE_SEGMENT_FILENAMES = -[ - './order.segment.json', - './product.segment.json' -]; - -const SOURCE_MODULE_FILENAMES = -[ - './order/createOrder.js', - './order/storeOrder.js', - './order/models.js', - './product/getProducts.js', - './product/getProducts_v1.js', - './product/models.js' -]; - -export { SOURCE_FILES, SOURCE_SEGMENT_FILENAMES, SOURCE_MODULE_FILENAMES }; diff --git a/packages/caching/test/_fixtures/TestFileManager.fixture.ts b/packages/caching/test/_fixtures/TestFileManager.fixture.ts deleted file mode 100644 index 0a3b924c..00000000 --- a/packages/caching/test/_fixtures/TestFileManager.fixture.ts +++ /dev/null @@ -1,83 +0,0 @@ - -import { File, FileManager, Files } from '@jitar/runtime'; - -import { SOURCE_FILES, SOURCE_SEGMENT_FILENAMES, SOURCE_MODULE_FILENAMES } from './SourceFiles.fixture'; - -class TestFileManager implements FileManager -{ - #writtenFiles: Map = new Map(); - - get writtenFiles() { return this.#writtenFiles; } - - getRootLocation(): string - { - return ''; - } - - getAbsoluteLocation(filename: string): string - { - if (filename.startsWith('./')) - { - return filename; - } - - return `./${filename}`; - } - - getRelativeLocation(filename: string): string - { - return filename.substring(2); - } - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - async getType(filename: string): Promise - { - return 'file'; - } - - async getContent(filename: string): Promise - { - const content = SOURCE_FILES[filename]; - - if (content === undefined) - { - throw new Error(`File not found: ${filename}`); - } - - return content; - } - - async read(filename: string): Promise - { - const absoluteFilename = this.getAbsoluteLocation(filename); - const type = await this.getType(filename); - const content = await this.getContent(filename); - - return new File(absoluteFilename, type, content); - } - - async write(filename: string, content: string): Promise - { - const absoluteFilename = this.getAbsoluteLocation(filename); - - this.#writtenFiles.set(absoluteFilename, content); - } - - delete(filename: string): Promise - { - throw new Error(`File not deleted: ${filename}`); - } - - async filter(pattern: string): Promise - { - switch (pattern) - { - case Files.SEGMENT_PATTERN: return SOURCE_SEGMENT_FILENAMES; - case Files.MODULE_PATTERN: return SOURCE_MODULE_FILENAMES; - } - - return []; - } -} - -export { TestFileManager }; diff --git a/packages/caching/test/_fixtures/building/ApplicationCacheBuilder.fixture.ts b/packages/caching/test/_fixtures/building/ApplicationCacheBuilder.fixture.ts deleted file mode 100644 index 0fb90826..00000000 --- a/packages/caching/test/_fixtures/building/ApplicationCacheBuilder.fixture.ts +++ /dev/null @@ -1,8 +0,0 @@ - -import { APPLICATION } from './models/Application.fixture'; -import { APPLICATION_CACHE } from './models/ApplicationCache.fixture'; - -const INPUT = APPLICATION; -const OUTPUT = APPLICATION_CACHE; - -export { INPUT, OUTPUT }; diff --git a/packages/caching/test/_fixtures/building/ApplicationCacheWriter.fixture.ts b/packages/caching/test/_fixtures/building/ApplicationCacheWriter.fixture.ts deleted file mode 100644 index a964c9d4..00000000 --- a/packages/caching/test/_fixtures/building/ApplicationCacheWriter.fixture.ts +++ /dev/null @@ -1,6 +0,0 @@ - -import { APPLICATION_CACHE } from './models/ApplicationCache.fixture'; - -const INPUT = APPLICATION_CACHE; - -export { INPUT }; diff --git a/packages/caching/test/_fixtures/building/ApplicationReader.fixture.ts b/packages/caching/test/_fixtures/building/ApplicationReader.fixture.ts deleted file mode 100644 index 572b38f0..00000000 --- a/packages/caching/test/_fixtures/building/ApplicationReader.fixture.ts +++ /dev/null @@ -1,13 +0,0 @@ - -import { APPLICATION } from './models/Application.fixture'; -import { SOURCE_SEGMENT_FILENAMES, SOURCE_MODULE_FILENAMES } from '../SourceFiles.fixture'; - -const INPUT = -{ - SEGMENT_FILENAMES: SOURCE_SEGMENT_FILENAMES, - MODULE_FILENAMES: SOURCE_MODULE_FILENAMES -}; - -const OUTPUT = APPLICATION; - -export { INPUT, OUTPUT }; diff --git a/packages/caching/test/_fixtures/building/ModuleCacheBuilder.fixture.ts b/packages/caching/test/_fixtures/building/ModuleCacheBuilder.fixture.ts deleted file mode 100644 index 8ed25652..00000000 --- a/packages/caching/test/_fixtures/building/ModuleCacheBuilder.fixture.ts +++ /dev/null @@ -1,14 +0,0 @@ - -import { APPLICATION } from './models/Application.fixture'; -import { MODULES } from './models/Module.fixture'; -import { MODULE_CACHES } from './models/ModuleCache.fixture'; - -const INPUT = -{ - APPLICATION: APPLICATION, - MODULE: MODULES.STORE_ORDER -}; - -const OUTPUT = MODULE_CACHES.STORE_ORDER; - -export { INPUT, OUTPUT }; diff --git a/packages/caching/test/_fixtures/building/ModuleCacheWriter.fixture.ts b/packages/caching/test/_fixtures/building/ModuleCacheWriter.fixture.ts deleted file mode 100644 index ad87c0b6..00000000 --- a/packages/caching/test/_fixtures/building/ModuleCacheWriter.fixture.ts +++ /dev/null @@ -1,51 +0,0 @@ - -import { MODULE_CACHES } from './models/ModuleCache.fixture'; -import { CACHE_FILES, CACHE_MODULE_FILENAMES } from '../CacheFiles.fixture'; - -const INPUT = -{ - CREATE_ORDER: MODULE_CACHES.CREATE_ORDER, - STORE_ORDER: MODULE_CACHES.STORE_ORDER, - ORDER_MODELS: MODULE_CACHES.ORDER_MODELS, - GET_PRODUCTS: MODULE_CACHES.GET_PRODUCTS, - GET_PRODUCTS_V1: MODULE_CACHES.GET_PRODUCTS_V1, - PRODUCT_MODELS: MODULE_CACHES.PRODUCT_MODELS -}; - -const OUTPUT = -{ - FILENAMES: - { - CREATE_ORDER_ORIGINAL: CACHE_MODULE_FILENAMES[0], - CREATE_ORDER_LOCAL: CACHE_MODULE_FILENAMES[1], - CREATE_ORDER_REMOTE: CACHE_MODULE_FILENAMES[2], - STORE_ORDER_ORIGINAL: CACHE_MODULE_FILENAMES[3], - STORE_ORDER_LOCAL: CACHE_MODULE_FILENAMES[4], - STORE_ORDER_REMOTE: CACHE_MODULE_FILENAMES[5], - ORDER_MODELS_ORIGINAL: CACHE_MODULE_FILENAMES[6], - ORDER_MODELS_LOCAL: CACHE_MODULE_FILENAMES[7], - GET_PRODUCTS_ORIGINAL: CACHE_MODULE_FILENAMES[8], - GET_PRODUCTS_LOCAL: CACHE_MODULE_FILENAMES[9], - GET_PRODUCTS_REMOTE: CACHE_MODULE_FILENAMES[10], - GET_PRODUCTS_ORIGINAL_V1: CACHE_MODULE_FILENAMES[11], - GET_PRODUCTS_LOCAL_V1: CACHE_MODULE_FILENAMES[12], - GET_PRODUCTS_REMOTE_V1: CACHE_MODULE_FILENAMES[13], - PRODUCT_MODELS_ORIGINAL: CACHE_MODULE_FILENAMES[14], - PRODUCT_MODELS_LOCAL: CACHE_MODULE_FILENAMES[15] - }, - CONTENT: - { - CREATE_ORDER_LOCAL: CACHE_FILES[CACHE_MODULE_FILENAMES[1]], - CREATE_ORDER_REMOTE: CACHE_FILES[CACHE_MODULE_FILENAMES[2]], - STORE_ORDER_LOCAL: CACHE_FILES[CACHE_MODULE_FILENAMES[4]], - STORE_ORDER_REMOTE: CACHE_FILES[CACHE_MODULE_FILENAMES[5]], - ORDER_MODELS_LOCAL: CACHE_FILES[CACHE_MODULE_FILENAMES[7]], - GET_PRODUCTS_LOCAL: CACHE_FILES[CACHE_MODULE_FILENAMES[9]], - GET_PRODUCTS_REMOTE: CACHE_FILES[CACHE_MODULE_FILENAMES[10]], - GET_PRODUCTS_LOCAL_V1: CACHE_FILES[CACHE_MODULE_FILENAMES[12]], - GET_PRODUCTS_REMOTE_V1: CACHE_FILES[CACHE_MODULE_FILENAMES[13]], - PRODUCT_MODELS_LOCAL: CACHE_FILES[CACHE_MODULE_FILENAMES[15]] - } -}; - -export { INPUT, OUTPUT }; diff --git a/packages/caching/test/_fixtures/building/ModuleReader.fixture.ts b/packages/caching/test/_fixtures/building/ModuleReader.fixture.ts deleted file mode 100644 index a11bf958..00000000 --- a/packages/caching/test/_fixtures/building/ModuleReader.fixture.ts +++ /dev/null @@ -1,7 +0,0 @@ - -import { MODULES } from './models/Module.fixture'; - -const INPUT = './order/storeOrder.js'; -const OUTPUT = MODULES.STORE_ORDER; - -export { INPUT, OUTPUT }; diff --git a/packages/caching/test/_fixtures/building/SegmentCacheBuilder.fixture.ts b/packages/caching/test/_fixtures/building/SegmentCacheBuilder.fixture.ts deleted file mode 100644 index e88d7c8e..00000000 --- a/packages/caching/test/_fixtures/building/SegmentCacheBuilder.fixture.ts +++ /dev/null @@ -1,8 +0,0 @@ - -import { SEGMENTS } from './models/Segment.fixture'; -import { SEGMENT_CACHES } from './models/SegmentCache.fixture'; - -const INPUT = SEGMENTS.ORDER; -const OUTPUT = SEGMENT_CACHES.ORDER; - -export { INPUT, OUTPUT }; diff --git a/packages/caching/test/_fixtures/building/SegmentCacheWriter.fixture.ts b/packages/caching/test/_fixtures/building/SegmentCacheWriter.fixture.ts deleted file mode 100644 index dbdaa863..00000000 --- a/packages/caching/test/_fixtures/building/SegmentCacheWriter.fixture.ts +++ /dev/null @@ -1,29 +0,0 @@ - -import { SEGMENT_CACHES } from './models/SegmentCache.fixture'; -import { CACHE_FILES, CACHE_SEGMENT_FILENAMES } from '../CacheFiles.fixture'; - -const INPUT = -{ - ORDER: SEGMENT_CACHES.ORDER, - PRODUCT: SEGMENT_CACHES.PRODUCT -}; - -const OUTPUT = -{ - FILENAMES: - { - ORDER_WORKER: CACHE_SEGMENT_FILENAMES[0], - ORDER_REPOSITORY: CACHE_SEGMENT_FILENAMES[1], - PRODUCT_WORKER: CACHE_SEGMENT_FILENAMES[2], - PRODUCT_REPOSITORY: CACHE_SEGMENT_FILENAMES[3] - }, - CONTENT: - { - ORDER_WORKER: CACHE_FILES[CACHE_SEGMENT_FILENAMES[0]], - ORDER_REPOSITORY: CACHE_FILES[CACHE_SEGMENT_FILENAMES[1]], - PRODUCT_WORKER: CACHE_FILES[CACHE_SEGMENT_FILENAMES[2]], - PRODUCT_REPOSITORY: CACHE_FILES[CACHE_SEGMENT_FILENAMES[3]] - } -}; - -export { INPUT, OUTPUT }; diff --git a/packages/caching/test/_fixtures/building/SegmentReader.fixture.ts b/packages/caching/test/_fixtures/building/SegmentReader.fixture.ts deleted file mode 100644 index 699352da..00000000 --- a/packages/caching/test/_fixtures/building/SegmentReader.fixture.ts +++ /dev/null @@ -1,7 +0,0 @@ - -import { SEGMENTS } from './models/Segment.fixture'; - -const INPUT = './order.segment.json'; -const OUTPUT = SEGMENTS.ORDER; - -export { INPUT, OUTPUT }; diff --git a/packages/caching/test/_fixtures/building/models/Application.fixture.ts b/packages/caching/test/_fixtures/building/models/Application.fixture.ts deleted file mode 100644 index 22f86fe7..00000000 --- a/packages/caching/test/_fixtures/building/models/Application.fixture.ts +++ /dev/null @@ -1,13 +0,0 @@ - -import Application from '../../../../src/building/models/Application'; - -import { SEGMENTS } from './Segment.fixture'; -import { MODULES } from './Module.fixture'; - -const APPLICATION = new Application -( - [SEGMENTS.ORDER, SEGMENTS.PRODUCT], - [MODULES.CREATE_ORDER, MODULES.STORE_ORDER, MODULES.GET_PRODUCTS, MODULES.GET_PRODUCTS_V1, MODULES.ORDER_MODELS, MODULES.PRODUCT_MODELS] -); - -export { APPLICATION }; diff --git a/packages/caching/test/_fixtures/building/models/ApplicationCache.fixture.ts b/packages/caching/test/_fixtures/building/models/ApplicationCache.fixture.ts deleted file mode 100644 index a1b7ea8c..00000000 --- a/packages/caching/test/_fixtures/building/models/ApplicationCache.fixture.ts +++ /dev/null @@ -1,13 +0,0 @@ - -import ApplicationCache from '../../../../src/building/models/ApplicationCache'; - -import { SEGMENT_CACHES } from './SegmentCache.fixture'; -import { MODULE_CACHES } from './ModuleCache.fixture'; - -const APPLICATION_CACHE = new ApplicationCache -( - [SEGMENT_CACHES.ORDER, SEGMENT_CACHES.PRODUCT], - [MODULE_CACHES.CREATE_ORDER, MODULE_CACHES.STORE_ORDER, MODULE_CACHES.GET_PRODUCTS, MODULE_CACHES.GET_PRODUCTS_V1, MODULE_CACHES.ORDER_MODELS, MODULE_CACHES.PRODUCT_MODELS], -); - -export { APPLICATION_CACHE }; diff --git a/packages/caching/test/_fixtures/building/models/Module.fixture.ts b/packages/caching/test/_fixtures/building/models/Module.fixture.ts deleted file mode 100644 index c083e6a6..00000000 --- a/packages/caching/test/_fixtures/building/models/Module.fixture.ts +++ /dev/null @@ -1,29 +0,0 @@ - -import { Reflector } from '@jitar/reflection'; - -import Module from '../../../../src/building/models/Module'; - -import { SOURCE_FILES } from '../../SourceFiles.fixture'; - -const reflector = new Reflector(); - -function createModule(filename: string): Module -{ - const relativeFilename = filename.substring(2); - const code = SOURCE_FILES[filename]; - const content = reflector.parse(code); - - return new Module(relativeFilename, code, content); -} - -const MODULES = -{ - CREATE_ORDER: createModule('./order/createOrder.js'), - STORE_ORDER: createModule('./order/storeOrder.js'), - GET_PRODUCTS: createModule('./product/getProducts.js'), - GET_PRODUCTS_V1: createModule('./product/getProducts_v1.js'), - ORDER_MODELS: createModule('./order/models.js'), - PRODUCT_MODELS: createModule('./product/models.js') -}; - -export { MODULES }; diff --git a/packages/caching/test/_fixtures/building/models/ModuleCache.fixture.ts b/packages/caching/test/_fixtures/building/models/ModuleCache.fixture.ts deleted file mode 100644 index 5dd8cb38..00000000 --- a/packages/caching/test/_fixtures/building/models/ModuleCache.fixture.ts +++ /dev/null @@ -1,17 +0,0 @@ - -import ModuleCache from '../../../../src/building/models/ModuleCache'; - -import { MODULES } from './Module.fixture'; -import { SEGMENT_MODULES } from './SegmentModule.fixture'; - -const MODULE_CACHES = -{ - CREATE_ORDER: new ModuleCache(MODULES.CREATE_ORDER, SEGMENT_MODULES.CREATE_ORDER), - STORE_ORDER: new ModuleCache(MODULES.STORE_ORDER, SEGMENT_MODULES.STORE_ORDER), - GET_PRODUCTS: new ModuleCache(MODULES.GET_PRODUCTS, SEGMENT_MODULES.GET_PRODUCTS), - GET_PRODUCTS_V1: new ModuleCache(MODULES.GET_PRODUCTS_V1, SEGMENT_MODULES.GET_PRODUCTS_V1), - ORDER_MODELS: new ModuleCache(MODULES.ORDER_MODELS, undefined), - PRODUCT_MODELS: new ModuleCache(MODULES.PRODUCT_MODELS, undefined) -}; - -export { MODULE_CACHES }; diff --git a/packages/caching/test/_fixtures/building/models/Segment.fixture.ts b/packages/caching/test/_fixtures/building/models/Segment.fixture.ts deleted file mode 100644 index aac6ebec..00000000 --- a/packages/caching/test/_fixtures/building/models/Segment.fixture.ts +++ /dev/null @@ -1,13 +0,0 @@ - -import Segment from '../../../../src/building/models/Segment'; - -import { SEGMENT_MODULES } from './SegmentModule.fixture'; - -const SEGMENTS = -{ - ORDER: new Segment('order', [SEGMENT_MODULES.CREATE_ORDER, SEGMENT_MODULES.STORE_ORDER]), - PRODUCT: new Segment('product', [SEGMENT_MODULES.GET_PRODUCTS, SEGMENT_MODULES.GET_PRODUCTS_V1]), - DUPLICATE: new Segment('duplicate', [SEGMENT_MODULES.DUPLICATE_MODULE]) -}; - -export { SEGMENTS }; diff --git a/packages/caching/test/_fixtures/building/models/SegmentCache.fixture.ts b/packages/caching/test/_fixtures/building/models/SegmentCache.fixture.ts deleted file mode 100644 index a6bef61f..00000000 --- a/packages/caching/test/_fixtures/building/models/SegmentCache.fixture.ts +++ /dev/null @@ -1,48 +0,0 @@ - -import SegmentCache from '../../../../src/building/models/SegmentCache'; -import SegmentImport from '../../../../src/building/models/SegmentImport'; - -import { SEGMENT_MODULES } from './SegmentModule.fixture'; -import { SEGMENT_PROCEDURES } from './SegmentProcedure.fixture'; - -const ORDER = new SegmentCache - ( - 'order', - [SEGMENT_MODULES.CREATE_ORDER.filename, SEGMENT_MODULES.STORE_ORDER.filename], - [ - new SegmentImport(['default : $1'], SEGMENT_MODULES.CREATE_ORDER.filename), - new SegmentImport(['v0_0_0 : $2', 'v1_0_0 : $3'], SEGMENT_MODULES.STORE_ORDER.filename) - ], - [SEGMENT_PROCEDURES.CREATE_ORDER, SEGMENT_PROCEDURES.STORE_ORDER] - ); - -const PRODUCT = new SegmentCache - ( - 'product', - [SEGMENT_MODULES.GET_PRODUCTS.filename], - [ - new SegmentImport(['default : $1', 'searchProducts : $2'], SEGMENT_MODULES.GET_PRODUCTS.filename), - new SegmentImport(['default : $3', 'searchProducts : $4'], SEGMENT_MODULES.GET_PRODUCTS_V1.filename) - ], - [SEGMENT_PROCEDURES.GET_PRODUCTS_MERGED, SEGMENT_PROCEDURES.SEARCH_PRODUCTS_MERGED] - ); - -const DUPLICATE = new SegmentCache - ( - 'duplicate', - [SEGMENT_MODULES.DUPLICATE_MODULE.filename], - [ - new SegmentImport(['v0_0_0 : $1', 'v0_0_0 : $2'], SEGMENT_MODULES.DUPLICATE_MODULE.filename), - new SegmentImport(['v1_0_0 : $3', 'v1_0_0 : $4'], SEGMENT_MODULES.DUPLICATE_MODULE.filename) - ], - [SEGMENT_PROCEDURES.DUPLICATE_PROCEDURES] - ); - -const SEGMENT_CACHES = -{ - ORDER, - PRODUCT, - DUPLICATE -}; - -export { SEGMENT_CACHES }; diff --git a/packages/caching/test/_fixtures/building/models/SegmentImplementation.fixture.ts b/packages/caching/test/_fixtures/building/models/SegmentImplementation.fixture.ts deleted file mode 100644 index 94c020f6..00000000 --- a/packages/caching/test/_fixtures/building/models/SegmentImplementation.fixture.ts +++ /dev/null @@ -1,27 +0,0 @@ - -import { ReflectionFunction } from '@jitar/reflection'; - -import SegmentImplementation from '../../../../src/building/models/SegmentImplementation'; - -import { MODULES } from './Module.fixture'; - -const CREATE_ORDER = MODULES.CREATE_ORDER.content.getFunction('createOrder') as ReflectionFunction; -const STORE_ORDER_V0 = MODULES.STORE_ORDER.content.getFunction('v0_0_0') as ReflectionFunction; -const STORE_ORDER_V1 = MODULES.STORE_ORDER.content.getFunction('v1_0_0') as ReflectionFunction; -const GET_PRODUCTS = MODULES.GET_PRODUCTS.content.getFunction('getProducts') as ReflectionFunction; -const SEARCH_PRODUCTS = MODULES.GET_PRODUCTS.content.getFunction('searchProducts') as ReflectionFunction; -const SEARCH_PRODUCTS_V1 = MODULES.GET_PRODUCTS_V1.content.getFunction('searchProducts') as ReflectionFunction; - -const SEGMENT_IMPLEMENTATIONS = -{ - CREATE_ORDER: new SegmentImplementation('$1', 'default', 'private', '0.0.0', CREATE_ORDER), - STORE_ORDER_V0: new SegmentImplementation('$2', 'v0_0_0', 'public', '0.0.0', STORE_ORDER_V0), - STORE_ORDER_V1: new SegmentImplementation('$3', 'v1_0_0', 'public', '1.0.0', STORE_ORDER_V1), - GET_PRODUCTS: new SegmentImplementation('$1', 'default', 'private', '0.0.0', GET_PRODUCTS), - SEARCH_PRODUCTS: new SegmentImplementation('$2', 'searchProducts', 'public', '0.0.0', SEARCH_PRODUCTS), - GET_PRODUCTS_V1: new SegmentImplementation('$3', 'default', 'private', '1.0.0', GET_PRODUCTS), - SEARCH_PRODUCTS_V1: new SegmentImplementation('$4', 'searchProducts', 'public', '1.0.0', SEARCH_PRODUCTS_V1), - STORE_ORDER_V0_DUPLICATE: new SegmentImplementation('$1', 'v0_0_0', 'public', '0.0.0', STORE_ORDER_V0), -}; - -export { SEGMENT_IMPLEMENTATIONS }; diff --git a/packages/caching/test/_fixtures/building/models/SegmentModule.fixture.ts b/packages/caching/test/_fixtures/building/models/SegmentModule.fixture.ts deleted file mode 100644 index 1960957e..00000000 --- a/packages/caching/test/_fixtures/building/models/SegmentModule.fixture.ts +++ /dev/null @@ -1,15 +0,0 @@ - -import SegmentModule from '../../../../src/building/models/SegmentModule'; - -import { SEGMENT_PROCEDURES } from './SegmentProcedure.fixture'; - -const SEGMENT_MODULES = -{ - CREATE_ORDER: new SegmentModule('order/createOrder.js', [SEGMENT_PROCEDURES.CREATE_ORDER]), - STORE_ORDER: new SegmentModule('order/storeOrder.js', [SEGMENT_PROCEDURES.STORE_ORDER]), - GET_PRODUCTS: new SegmentModule('product/getProducts.js', [SEGMENT_PROCEDURES.GET_PRODUCTS, SEGMENT_PROCEDURES.SEARCH_PRODUCTS]), - GET_PRODUCTS_V1: new SegmentModule('product/getProducts_v1.js', [SEGMENT_PROCEDURES.GET_PRODUCTS_V1, SEGMENT_PROCEDURES.SEARCH_PRODUCTS_V1]), - DUPLICATE_MODULE: new SegmentModule('order/storeOrder.js', [SEGMENT_PROCEDURES.DUPLICATE_PROCEDURES]) -}; - -export { SEGMENT_MODULES }; diff --git a/packages/caching/test/_fixtures/building/models/SegmentProcedure.fixture.ts b/packages/caching/test/_fixtures/building/models/SegmentProcedure.fixture.ts deleted file mode 100644 index 2d909821..00000000 --- a/packages/caching/test/_fixtures/building/models/SegmentProcedure.fixture.ts +++ /dev/null @@ -1,20 +0,0 @@ - -import SegmentProcedure from '../../../../src/building/models/SegmentProcedure'; - -import { SEGMENT_IMPLEMENTATIONS } from './SegmentImplementation.fixture'; -import { CONSTANTS } from '../../Constants.fixture'; - -const SEGMENT_PROCEDURES = -{ - CREATE_ORDER: new SegmentProcedure(CONSTANTS.CREATE_ORDER_FQN, [SEGMENT_IMPLEMENTATIONS.CREATE_ORDER]), - STORE_ORDER: new SegmentProcedure(CONSTANTS.STORE_ORDER_FQN, [SEGMENT_IMPLEMENTATIONS.STORE_ORDER_V0, SEGMENT_IMPLEMENTATIONS.STORE_ORDER_V1]), - GET_PRODUCTS: new SegmentProcedure(CONSTANTS.GET_PRODUCTS_FQN, [SEGMENT_IMPLEMENTATIONS.GET_PRODUCTS]), - GET_PRODUCTS_V1: new SegmentProcedure(CONSTANTS.GET_PRODUCTS_FQN, [SEGMENT_IMPLEMENTATIONS.GET_PRODUCTS_V1]), - GET_PRODUCTS_MERGED: new SegmentProcedure(CONSTANTS.GET_PRODUCTS_FQN, [SEGMENT_IMPLEMENTATIONS.GET_PRODUCTS, SEGMENT_IMPLEMENTATIONS.GET_PRODUCTS_V1]), - SEARCH_PRODUCTS: new SegmentProcedure(CONSTANTS.SEARCH_PRODUCTS_FQN, [SEGMENT_IMPLEMENTATIONS.SEARCH_PRODUCTS]), - SEARCH_PRODUCTS_V1: new SegmentProcedure(CONSTANTS.SEARCH_PRODUCTS_FQN, [SEGMENT_IMPLEMENTATIONS.SEARCH_PRODUCTS_V1]), - SEARCH_PRODUCTS_MERGED: new SegmentProcedure(CONSTANTS.SEARCH_PRODUCTS_FQN, [SEGMENT_IMPLEMENTATIONS.SEARCH_PRODUCTS, SEGMENT_IMPLEMENTATIONS.SEARCH_PRODUCTS_V1]), - DUPLICATE_PROCEDURES: new SegmentProcedure(CONSTANTS.STORE_ORDER_FQN, [SEGMENT_IMPLEMENTATIONS.STORE_ORDER_V0, SEGMENT_IMPLEMENTATIONS.STORE_ORDER_V0_DUPLICATE]) -}; - -export { SEGMENT_PROCEDURES }; diff --git a/packages/caching/test/_fixtures/building/utils/ImportRewriter.fixture.ts b/packages/caching/test/_fixtures/building/utils/ImportRewriter.fixture.ts deleted file mode 100644 index e298c729..00000000 --- a/packages/caching/test/_fixtures/building/utils/ImportRewriter.fixture.ts +++ /dev/null @@ -1,96 +0,0 @@ - -// With .js extension -const APPLICATION_IMPORTS = -`import './path/to/file.js'; -import component from '/path/to/component.js'; -import { component1, component2 } from '../path/to/components.js'; -`; - -const APPLICATION_IMPORTS_RESULT = -`await __import("./components/path/to/file.js", "application"); -const component = await __import("./components/path/to/component.js", "application", true); -const { component1, component2 } = await __import("./path/to/components.js", "application", false); -`; - -const RUNTIME_IMPORTS = -`import 'polyfills'; -import * as fs from 'fs'; -import component from 'http'; -import { component1, component2 } from 'https'; -`; - -const RUNTIME_IMPORTS_RESULT = -`await __import("polyfills", "runtime"); -const fs = await __import("fs", "runtime", true); -const component = await __import("http", "runtime", true); -const { component1, component2 } = await __import("https", "runtime", false); -`; - -const IMPORT_WITHOUT_SEMICOLON = `import { runProcedure } from 'jitar'`; - -const IMPORT_WITHOUT_SEMICOLON_RESULT = `const { runProcedure } = await __import("jitar", "runtime", false);`; - -// Without .js extension -const MIXED_IMPORTS = -`import component from './path/to/component'; -import os from 'os'; -import { runProcedure } from 'jitar'; -import main, { some as other } from 'library'; -`; - -const MIXED_IMPORTS_RESULT = -`const component = await __import("./components/path/to/component.js", "application", true); -const os = await __import("os", "runtime", true); -const { runProcedure } = await __import("jitar", "runtime", false); -const { default : main, some : other } = await __import("library", "runtime", false); -`; - -const DYNAMIC_IMPORTS = -`import component from './path/to/component.js'; -const os = await import('os'); -`; - -const DYNAMIC_IMPORTS_RESULT = -`const component = await __import("./components/path/to/component.js", "application", true); -const os = await import('os'); -`; - -const IMPORTS_AND_CONTENT = -`import os from 'os'; - -export default function test() {} - -import { runProcedure } from 'jitar'; -`; - -const IMPORTS_AND_CONTENT_RESULT = -`const os = await __import("os", "runtime", true); - -export default function test() {} - -const { runProcedure } = await __import("jitar", "runtime", false); -`; - -const INPUTS = -{ - APPLICATION_IMPORTS, - RUNTIME_IMPORTS, - IMPORT_WITHOUT_SEMICOLON, - MIXED_IMPORTS, - DYNAMIC_IMPORTS, - IMPORTS_AND_CONTENT -}; - -const OUTPUTS = -{ - APPLICATION_IMPORTS_RESULT, - RUNTIME_IMPORTS_RESULT, - IMPORT_WITHOUT_SEMICOLON_RESULT, - MIXED_IMPORTS_RESULT, - DYNAMIC_IMPORTS_RESULT, - IMPORTS_AND_CONTENT_RESULT -}; - -const SOURCE_FILE = './components/test.js'; - -export { INPUTS, OUTPUTS, SOURCE_FILE }; diff --git a/packages/caching/test/_fixtures/building/utils/RemoteBuilder.fixture.ts b/packages/caching/test/_fixtures/building/utils/RemoteBuilder.fixture.ts deleted file mode 100644 index d76aa7bc..00000000 --- a/packages/caching/test/_fixtures/building/utils/RemoteBuilder.fixture.ts +++ /dev/null @@ -1,8 +0,0 @@ - -import { SEGMENT_MODULES } from '../models/SegmentModule.fixture'; -import { CACHE_FILES } from '../../CacheFiles.fixture'; - -const INPUT = SEGMENT_MODULES.GET_PRODUCTS; -const OUTPUT = CACHE_FILES['./product/getProducts.remote.js']; - -export { INPUT, OUTPUT }; diff --git a/packages/caching/test/building/ApplicationCacheBuilder.spec.ts b/packages/caching/test/building/ApplicationCacheBuilder.spec.ts deleted file mode 100644 index b772e00a..00000000 --- a/packages/caching/test/building/ApplicationCacheBuilder.spec.ts +++ /dev/null @@ -1,32 +0,0 @@ - -import { describe, expect, it } from 'vitest'; - -import ApplicationCacheBuilder from '../../src/building/ApplicationCacheBuilder'; - -import { INPUT, OUTPUT } from '../_fixtures/building/ApplicationCacheBuilder.fixture'; - -const applicationCacheBuilder = new ApplicationCacheBuilder(); - -describe('building/ApplicationCacheBuilder', () => -{ - describe('.build(application)', () => - { - it('should build a cache model for an application', () => - { - const result = applicationCacheBuilder.build(INPUT); - - // Check if the segments are correctly created - expect(result.segments).toHaveLength(OUTPUT.segments.length); - expect(result.segments[0].name).toEqual(OUTPUT.segments[0].name); - expect(result.segments[1].name).toEqual(OUTPUT.segments[1].name); - - // Check if the modules are correctly created - expect(result.modules).toHaveLength(OUTPUT.modules.length); - expect(result.modules[0].module.filename).toEqual(OUTPUT.modules[0].module.filename); - expect(result.modules[1].module.filename).toEqual(OUTPUT.modules[1].module.filename); - expect(result.modules[2].module.filename).toEqual(OUTPUT.modules[2].module.filename); - expect(result.modules[3].module.filename).toEqual(OUTPUT.modules[3].module.filename); - expect(result.modules[4].module.filename).toEqual(OUTPUT.modules[4].module.filename); - }); - }); -}); diff --git a/packages/caching/test/building/ApplicationCacheWriter.spec.ts b/packages/caching/test/building/ApplicationCacheWriter.spec.ts deleted file mode 100644 index d27fc83a..00000000 --- a/packages/caching/test/building/ApplicationCacheWriter.spec.ts +++ /dev/null @@ -1,27 +0,0 @@ - -import { describe, expect, it } from 'vitest'; - -import ApplicationCacheWriter from '../../src/building/ApplicationCacheWriter'; - -import { TestFileManager } from '../_fixtures/TestFileManager.fixture'; -import { INPUT } from '../_fixtures/building/ApplicationCacheWriter.fixture'; - -describe('building/ApplicationCacheWriter', () => -{ - describe('.write(cache)', () => - { - it('should write application cache files', async () => - { - // We need to create a new file manager for each test, because the file manager - // keeps track of the files that are written to disk. - - const fileManager = new TestFileManager(); - const applicationCacheWriter = new ApplicationCacheWriter(fileManager); - - await applicationCacheWriter.write(INPUT); - - const result = fileManager.writtenFiles; - expect(result.size).toBe(14); - }); - }); -}); diff --git a/packages/caching/test/building/ApplicationReader.spec.ts b/packages/caching/test/building/ApplicationReader.spec.ts deleted file mode 100644 index 72dcde80..00000000 --- a/packages/caching/test/building/ApplicationReader.spec.ts +++ /dev/null @@ -1,31 +0,0 @@ - -import { describe, expect, it } from 'vitest'; - -import ApplicationReader from '../../src/building/ApplicationReader'; - -import { TestFileManager } from '../_fixtures/TestFileManager.fixture'; -import { INPUT, OUTPUT } from '../_fixtures/building/ApplicationReader.fixture'; - -const fileManager = new TestFileManager(); -const applicationReader = new ApplicationReader(fileManager); - -describe('building/ApplicationReader', () => -{ - describe('.read(segmentFiles, moduleFiles)', () => - { - it('should read an application from its sources', async () => - { - const result = await applicationReader.read(INPUT.SEGMENT_FILENAMES, INPUT.MODULE_FILENAMES); - expect(result.segments).toHaveLength(OUTPUT.segments.length); - expect(result.modules).toHaveLength(OUTPUT.modules.length); - - const firstSegmentResult = result.segments[0]; - const firstSegmentOutput = OUTPUT.segments[0]; - expect(firstSegmentResult.name).toEqual(firstSegmentOutput.name); - - const firstModuleResult = result.modules[0]; - const firstModuleOutput = OUTPUT.modules[0]; - expect(firstModuleResult.filename).toEqual(firstModuleOutput.filename); - }); - }); -}); diff --git a/packages/caching/test/building/ModuleCacheBuilder.spec.ts b/packages/caching/test/building/ModuleCacheBuilder.spec.ts deleted file mode 100644 index e39081ec..00000000 --- a/packages/caching/test/building/ModuleCacheBuilder.spec.ts +++ /dev/null @@ -1,23 +0,0 @@ - -import { describe, expect, it } from 'vitest'; - -import ModuleCacheBuilder from '../../src/building/ModuleCacheBuilder'; - -import { INPUT, OUTPUT } from '../_fixtures/building/ModuleCacheBuilder.fixture'; - -const moduleCacheBuilder = new ModuleCacheBuilder(); - -describe('building/ModuleCacheBuilder', () => -{ - describe('.build(application, module)', () => - { - it('should build a cache model for a module', () => - { - const result = moduleCacheBuilder.build(INPUT.APPLICATION, INPUT.MODULE); - - // Check if the correct module and segment are set - expect(result.module.filename).toEqual(OUTPUT.module.filename); - expect(result.segment?.filename).toEqual(OUTPUT.segment?.filename); - }); - }); -}); diff --git a/packages/caching/test/building/ModuleCacheWriter.spec.ts b/packages/caching/test/building/ModuleCacheWriter.spec.ts deleted file mode 100644 index bb44c132..00000000 --- a/packages/caching/test/building/ModuleCacheWriter.spec.ts +++ /dev/null @@ -1,49 +0,0 @@ - -import { describe, expect, it } from 'vitest'; - -import ModuleCacheWriter from '../../src/building/ModuleCacheWriter'; - -import { TestFileManager } from '../_fixtures/TestFileManager.fixture'; -import { CACHE_MODULE_FILENAMES } from '../_fixtures/CacheFiles.fixture'; -import { INPUT, OUTPUT } from '../_fixtures/building/ModuleCacheWriter.fixture'; - -describe('building/ModuleCacheWriter', () => -{ - describe('.write(cache)', () => - { - it('should write module cache files', async () => - { - // We need to create a new file manager for each test, because the file manager - // keeps track of the files that are written to disk. - - const fileManager = new TestFileManager(); - const moduleCacheWriter = new ModuleCacheWriter(fileManager); - - await moduleCacheWriter.write(INPUT.CREATE_ORDER); - await moduleCacheWriter.write(INPUT.STORE_ORDER); - await moduleCacheWriter.write(INPUT.ORDER_MODELS); - await moduleCacheWriter.write(INPUT.GET_PRODUCTS); - await moduleCacheWriter.write(INPUT.GET_PRODUCTS_V1); - await moduleCacheWriter.write(INPUT.PRODUCT_MODELS); - - const result = fileManager.writtenFiles; - expect(result.size).toBe(CACHE_MODULE_FILENAMES.length); - - expect(result.get(OUTPUT.FILENAMES.CREATE_ORDER_LOCAL)).toBe(OUTPUT.CONTENT.CREATE_ORDER_LOCAL); - expect(result.get(OUTPUT.FILENAMES.CREATE_ORDER_REMOTE)).toBe(OUTPUT.CONTENT.CREATE_ORDER_REMOTE); - - expect(result.get(OUTPUT.FILENAMES.STORE_ORDER_LOCAL)).toBe(OUTPUT.CONTENT.STORE_ORDER_LOCAL); - expect(result.get(OUTPUT.FILENAMES.STORE_ORDER_REMOTE)).toBe(OUTPUT.CONTENT.STORE_ORDER_REMOTE); - - expect(result.get(OUTPUT.FILENAMES.ORDER_MODELS_LOCAL)).toBe(OUTPUT.CONTENT.ORDER_MODELS_LOCAL); - - expect(result.get(OUTPUT.FILENAMES.GET_PRODUCTS_LOCAL)).toBe(OUTPUT.CONTENT.GET_PRODUCTS_LOCAL); - expect(result.get(OUTPUT.FILENAMES.GET_PRODUCTS_REMOTE)).toBe(OUTPUT.CONTENT.GET_PRODUCTS_REMOTE); - - expect(result.get(OUTPUT.FILENAMES.GET_PRODUCTS_LOCAL_V1)).toBe(OUTPUT.CONTENT.GET_PRODUCTS_LOCAL_V1); - expect(result.get(OUTPUT.FILENAMES.GET_PRODUCTS_REMOTE_V1)).toBe(OUTPUT.CONTENT.GET_PRODUCTS_REMOTE_V1); - - expect(result.get(OUTPUT.FILENAMES.PRODUCT_MODELS_LOCAL)).toBe(OUTPUT.CONTENT.PRODUCT_MODELS_LOCAL); - }); - }); -}); diff --git a/packages/caching/test/building/ModuleReader.spec.ts b/packages/caching/test/building/ModuleReader.spec.ts deleted file mode 100644 index 3bd66c5f..00000000 --- a/packages/caching/test/building/ModuleReader.spec.ts +++ /dev/null @@ -1,24 +0,0 @@ - -import { describe, expect, it } from 'vitest'; - -import ModuleReader from '../../src/building/ModuleReader'; - -import { TestFileManager } from '../_fixtures/TestFileManager.fixture'; -import { INPUT, OUTPUT } from '../_fixtures/building/ModuleReader.fixture'; - -const fileManager = new TestFileManager(); -const moduleReader = new ModuleReader(fileManager); - -describe('building/ModuleReader', () => -{ - describe('.read(filename)', () => - { - it('should read a module from a source file', async () => - { - const result = await moduleReader.read(INPUT); - - expect(result.filename).toEqual(OUTPUT.filename); - expect(result.code).toEqual(OUTPUT.code); - }); - }); -}); diff --git a/packages/caching/test/building/SegmentCacheBuilder.spec.ts b/packages/caching/test/building/SegmentCacheBuilder.spec.ts deleted file mode 100644 index 866218ba..00000000 --- a/packages/caching/test/building/SegmentCacheBuilder.spec.ts +++ /dev/null @@ -1,45 +0,0 @@ - -import { describe, expect, it } from 'vitest'; - -import SegmentCacheBuilder from '../../src/building/SegmentCacheBuilder'; -import DuplicateImplementation from '../../src/building/errors/DuplicateImplementation'; - -import { INPUT, OUTPUT } from '../_fixtures/building/SegmentCacheBuilder.fixture'; -import { SEGMENTS } from '../_fixtures/building/models/Segment.fixture'; -import { CONSTANTS } from '../_fixtures/Constants.fixture'; - -const segmentCacheBuilder = new SegmentCacheBuilder(); - -describe('building/SegmentCacheBuilder', () => -{ - describe('.build(segment)', () => - { - it('should build a cache model for a segment', () => - { - const result = segmentCacheBuilder.build(INPUT); - expect(result.name).toEqual(OUTPUT.name); - - // Check if the files are correctly populated - expect(result.files).toEqual(OUTPUT.files); - - // Check if the imports are correctly created - expect(result.imports).toHaveLength(OUTPUT.imports.length); - expect(result.imports[0].members).toEqual(OUTPUT.imports[0].members); - expect(result.imports[0].from).toEqual(OUTPUT.imports[0].from); - expect(result.imports[1].members).toEqual(OUTPUT.imports[1].members); - expect(result.imports[1].from).toEqual(OUTPUT.imports[1].from); - - // Check if the procedures are merged - expect(result.procedures).toHaveLength(OUTPUT.procedures.length); - expect(result.procedures[0].fqn).toEqual(OUTPUT.procedures[0].fqn); - expect(result.procedures[1].fqn).toEqual(OUTPUT.procedures[1].fqn); - }); - - it('should throw an error if the procedure contain a duplicate implementation', () => - { - const run = () => segmentCacheBuilder.build(SEGMENTS.DUPLICATE); - - expect(run).toThrowError(new DuplicateImplementation(CONSTANTS.STORE_ORDER_FQN, '0.0.0')); - }); - }); -}); diff --git a/packages/caching/test/building/SegmentCacheWriter.spec.ts b/packages/caching/test/building/SegmentCacheWriter.spec.ts deleted file mode 100644 index b1c39e86..00000000 --- a/packages/caching/test/building/SegmentCacheWriter.spec.ts +++ /dev/null @@ -1,34 +0,0 @@ - -import { describe, expect, it } from 'vitest'; - -import SegmentCacheWriter from '../../src/building/SegmentCacheWriter'; - -import { TestFileManager } from '../_fixtures/TestFileManager.fixture'; -import { INPUT, OUTPUT } from '../_fixtures/building/SegmentCacheWriter.fixture'; - -describe('building/SegmentCacheWriter', () => -{ - describe('.write(cache)', () => - { - it('should write segment cache files', async () => - { - // We need to create a new file manager for each test, because the file manager - // keeps track of the files that are written to disk. - - const fileManager = new TestFileManager(); - const segmentCacheWriter = new SegmentCacheWriter(fileManager); - - await segmentCacheWriter.write(INPUT.ORDER); - await segmentCacheWriter.write(INPUT.PRODUCT); - - const result = fileManager.writtenFiles; - expect(result.size).toBe(4); - - expect(result.get(OUTPUT.FILENAMES.ORDER_WORKER)).toBe(OUTPUT.CONTENT.ORDER_WORKER); - expect(result.get(OUTPUT.FILENAMES.ORDER_REPOSITORY)).toBe(OUTPUT.CONTENT.ORDER_REPOSITORY); - - expect(result.get(OUTPUT.FILENAMES.PRODUCT_WORKER)).toBe(OUTPUT.CONTENT.PRODUCT_WORKER); - expect(result.get(OUTPUT.FILENAMES.PRODUCT_REPOSITORY)).toBe(OUTPUT.CONTENT.PRODUCT_REPOSITORY); - }); - }); -}); diff --git a/packages/caching/test/building/SegmentReader.spec.ts b/packages/caching/test/building/SegmentReader.spec.ts deleted file mode 100644 index 3faf23b7..00000000 --- a/packages/caching/test/building/SegmentReader.spec.ts +++ /dev/null @@ -1,57 +0,0 @@ - -import { describe, expect, it } from 'vitest'; - -import SegmentReader from '../../src/building/SegmentReader'; - -import { TestFileManager } from '../_fixtures/TestFileManager.fixture'; -import { INPUT, OUTPUT } from '../_fixtures/building/SegmentReader.fixture'; - -const fileManager = new TestFileManager(); -const segmentReader = new SegmentReader(fileManager); - -describe('building/SegmentReader', () => -{ - describe('.read(filename)', () => - { - it('should read a segment from a definition file', async () => - { - const result = await segmentReader.read(INPUT); - expect(result.name).toEqual(OUTPUT.name); - expect(result.modules).toHaveLength(OUTPUT.modules.length); - - const firstModuleResult = result.modules[0]; - const firstModuleOutput = OUTPUT.modules[0]; - expect(firstModuleResult.filename).toEqual(firstModuleOutput.filename); - expect(firstModuleResult.procedures).toHaveLength(firstModuleOutput.procedures.length); - - const firstProcedureResult = firstModuleResult.procedures[0]; - const firstProcedureOutput = firstModuleOutput.procedures[0]; - expect(firstProcedureResult.fqn).toEqual(firstProcedureOutput.fqn); - expect(firstProcedureResult.implementations).toHaveLength(firstProcedureOutput.implementations.length); - - const firstImplementationResult = firstProcedureResult.implementations[0]; - const firstImplementationOutput = firstProcedureOutput.implementations[0]; - expect(firstImplementationResult.id).toEqual(firstImplementationOutput.id); - expect(firstImplementationResult.importKey).toEqual(firstImplementationOutput.importKey); - expect(firstImplementationResult.access).toEqual(firstImplementationOutput.access); - expect(firstImplementationResult.version).toEqual(firstImplementationOutput.version); - - const secondModuleResult = result.modules[0]; - const secondModuleOutput = OUTPUT.modules[0]; - expect(secondModuleResult.filename).toEqual(secondModuleOutput.filename); - expect(secondModuleResult.procedures).toHaveLength(secondModuleOutput.procedures.length); - - const secondProcedureResult = secondModuleResult.procedures[0]; - const secondProcedureOutput = secondModuleOutput.procedures[0]; - expect(secondProcedureResult.fqn).toEqual(secondProcedureOutput.fqn); - expect(secondProcedureResult.implementations).toHaveLength(secondProcedureOutput.implementations.length); - - const secondImplementationResult = secondProcedureResult.implementations[0]; - const secondImplementationOutput = secondProcedureOutput.implementations[0]; - expect(secondImplementationResult.id).toEqual(secondImplementationOutput.id); - expect(secondImplementationResult.importKey).toEqual(secondImplementationOutput.importKey); - expect(secondImplementationResult.access).toEqual(secondImplementationOutput.access); - expect(secondImplementationResult.version).toEqual(secondImplementationOutput.version); - }); - }); -}); diff --git a/packages/caching/test/building/models/Application.spec.ts b/packages/caching/test/building/models/Application.spec.ts deleted file mode 100644 index 91e041d8..00000000 --- a/packages/caching/test/building/models/Application.spec.ts +++ /dev/null @@ -1,26 +0,0 @@ - -import { describe, expect, it } from 'vitest'; - -import { APPLICATION } from '../../_fixtures/building/models/Application.fixture'; - -const application = APPLICATION; - -describe('building/models/Application', () => -{ - describe('.getSegmentModule(filename)', () => - { - it('should get an existing segment module', () => - { - const result = application.getSegmentModule('order/createOrder.js'); - - expect(result).toBeDefined(); - }); - - it('should not get an non-existing segment module', () => - { - const result = application.getSegmentModule('non-existing.js'); - - expect(result).toBeUndefined(); - }); - }); -}); diff --git a/packages/caching/test/building/models/Segment.spec.ts b/packages/caching/test/building/models/Segment.spec.ts deleted file mode 100644 index 88bf48da..00000000 --- a/packages/caching/test/building/models/Segment.spec.ts +++ /dev/null @@ -1,43 +0,0 @@ - -import { describe, expect, it } from 'vitest'; - -import { SEGMENTS } from '../../_fixtures/building/models/Segment.fixture'; - -const segment = SEGMENTS.ORDER; - -describe('building/models/Segment', () => -{ - describe('.hasModule(filename)', () => - { - it('should have an existing module', () => - { - const result = segment.hasModule('order/createOrder.js'); - - expect(result).toBeTruthy(); - }); - - it('should not have an non-existing module', () => - { - const result = segment.hasModule('non-existing.js'); - - expect(result).toBeFalsy(); - }); - }); - - describe('.getModule(filename)', () => - { - it('should get an existing module', () => - { - const result = segment.getModule('order/createOrder.js'); - - expect(result).toBeDefined(); - }); - - it('should not get an non-existing module', () => - { - const result = segment.getModule('non-existing.js'); - - expect(result).toBeUndefined(); - }); - }); -}); diff --git a/packages/caching/test/building/utils/IdGenerator.spec.ts b/packages/caching/test/building/utils/IdGenerator.spec.ts deleted file mode 100644 index a26e1e66..00000000 --- a/packages/caching/test/building/utils/IdGenerator.spec.ts +++ /dev/null @@ -1,23 +0,0 @@ - -import { describe, expect, it } from 'vitest'; - -import IdGenerator from '../../../src/building/utils/IdGenerator'; - -describe('building/utils/IdGenerator', () => -{ - describe('.next()', () => - { - it('should create incremental ids', () => - { - const generator = new IdGenerator(); - - const first = generator.next(); - const second = generator.next(); - const third = generator.next(); - - expect(first).toBe('$1'); - expect(second).toBe('$2'); - expect(third).toBe('$3'); - }); - }); -}); diff --git a/packages/caching/test/building/utils/ImportRewriter.spec.ts b/packages/caching/test/building/utils/ImportRewriter.spec.ts deleted file mode 100644 index 5653a860..00000000 --- a/packages/caching/test/building/utils/ImportRewriter.spec.ts +++ /dev/null @@ -1,56 +0,0 @@ - -import { describe, expect, it } from 'vitest'; - -import ImportRewriter from '../../../src/building/utils/ImportRewriter'; - -import { INPUTS, OUTPUTS, SOURCE_FILE } from '../../_fixtures/building/utils/ImportRewriter.fixture'; - -const importRewriter = new ImportRewriter(); - -describe('building/utils/ImportRewriter', () => -{ - describe('.rewrite(module)', () => - { - it('should rewrite application imports', () => - { - const result = importRewriter.rewrite(INPUTS.APPLICATION_IMPORTS, SOURCE_FILE); - - expect(result).toBe(OUTPUTS.APPLICATION_IMPORTS_RESULT); - }); - - it('should rewrite runtime imports', () => - { - const result = importRewriter.rewrite(INPUTS.RUNTIME_IMPORTS, SOURCE_FILE); - - expect(result).toBe(OUTPUTS.RUNTIME_IMPORTS_RESULT); - }); - - it('should rewrite imports without closing semicolon', () => - { - const result = importRewriter.rewrite(INPUTS.IMPORT_WITHOUT_SEMICOLON, SOURCE_FILE); - - expect(result).toBe(OUTPUTS.IMPORT_WITHOUT_SEMICOLON_RESULT); - }); - - it('should rewrite application and runtime imports', () => - { - const result = importRewriter.rewrite(INPUTS.MIXED_IMPORTS, SOURCE_FILE); - - expect(result).toBe(OUTPUTS.MIXED_IMPORTS_RESULT); - }); - - it('should not rewrite dynamic imports', () => - { - const result = importRewriter.rewrite(INPUTS.DYNAMIC_IMPORTS, SOURCE_FILE); - - expect(result).toBe(OUTPUTS.DYNAMIC_IMPORTS_RESULT); - }); - - it('should not modify any content', () => - { - const result = importRewriter.rewrite(INPUTS.IMPORTS_AND_CONTENT, SOURCE_FILE); - - expect(result).toBe(OUTPUTS.IMPORTS_AND_CONTENT_RESULT); - }); - }); -}); diff --git a/packages/caching/test/building/utils/RemoteBuilder.spec.ts b/packages/caching/test/building/utils/RemoteBuilder.spec.ts deleted file mode 100644 index a32c32f5..00000000 --- a/packages/caching/test/building/utils/RemoteBuilder.spec.ts +++ /dev/null @@ -1,21 +0,0 @@ - -import { describe, expect, it } from 'vitest'; - -import RemoteBuilder from '../../../src/building/utils/RemoteBuilder'; - -import { INPUT, OUTPUT } from '../../_fixtures/building/utils/RemoteBuilder.fixture'; - -const remoteBuilder = new RemoteBuilder(); - -describe('building/utils/RemoteBuilder', () => -{ - describe('.build(module)', () => - { - it('should create remote calls for all procedure implementations', () => - { - const result = remoteBuilder.build(INPUT); - - expect(result).toBe(OUTPUT); - }); - }); -}); diff --git a/packages/server-nodejs/CHANGELOG.md b/packages/cli/CHANGELOG.md similarity index 100% rename from packages/server-nodejs/CHANGELOG.md rename to packages/cli/CHANGELOG.md diff --git a/packages/cli/README.md b/packages/cli/README.md new file mode 100644 index 00000000..3ec05761 --- /dev/null +++ b/packages/cli/README.md @@ -0,0 +1,9 @@ + +# Jitar CLI + +This package provides the CLI interface for the [Jitar](https://jitar.dev) runtime. + +For more information about Jitar: + +* [Visit our website](https://jitar.dev) +* [Read the documentation](https://docs.jitar.dev). diff --git a/packages/cli/package.json b/packages/cli/package.json new file mode 100644 index 00000000..412f5174 --- /dev/null +++ b/packages/cli/package.json @@ -0,0 +1,25 @@ +{ + "name": "@jitar/cli", + "version": "0.7.4", + "description": "CLI tool for the Jitar runtime.", + "author": "Masking Technology (https://jitar.dev)", + "license": "MIT", + "type": "module", + "types": "dist/index.d.ts", + "exports": "./dist/index.js", + "private": true, + "scripts": { + "test": "vitest run", + "test-coverage": "vitest run --coverage", + "lint": "eslint . --ext .ts", + "build": "tsc -p tsconfig.json", + "clean": "rm -rf dist" + }, + "dependencies": { + "@jitar/build": "*", + "@jitar/configuration": "*", + "@jitar/sourcing": "*", + "@jitar/http": "*", + "@jitar/runtime": "*" + } +} diff --git a/packages/cli/src/ArgumentProcessor.ts b/packages/cli/src/ArgumentProcessor.ts new file mode 100644 index 00000000..c8bc7a08 --- /dev/null +++ b/packages/cli/src/ArgumentProcessor.ts @@ -0,0 +1,54 @@ + +import MissingArgument from './errors/MissingArgument'; + +const COMMAND_INDEX = 2; + +export default class ArgumentProcessor +{ + #command: string | undefined; + #args: Map; + + constructor(args: string[]) + { + this.#command = args[COMMAND_INDEX]; + this.#args = this.#parseArguments(args); + } + + getCommand(): string | undefined + { + return this.#command; + } + + getRequiredArgument(name: string): string + { + const value = this.#args.get(name); + + if (value === undefined) + { + throw new MissingArgument(name); + } + + return value; + } + + getOptionalArgument(name: string, defaultValue: T): T + { + return this.#args.get(name) as T ?? defaultValue; + } + + #parseArguments(args: string[]): Map + { + const commandArgs = args.slice(COMMAND_INDEX + 1); + + const map = new Map(); + + commandArgs.forEach((arg) => + { + const [key, value] = arg.split('='); + + map.set(key.trim(), value?.trim()); + }); + + return map; + } +} diff --git a/packages/cli/src/Cli.ts b/packages/cli/src/Cli.ts new file mode 100644 index 00000000..6be5760a --- /dev/null +++ b/packages/cli/src/Cli.ts @@ -0,0 +1,22 @@ + +import ArgumentProcessor from './ArgumentProcessor'; +import CommandRunner from './CommandRunner'; + +export default class Cli +{ + #argumentProcessor: ArgumentProcessor; + #commandRunner: CommandRunner; + + constructor() + { + this.#argumentProcessor = new ArgumentProcessor(process.argv); + this.#commandRunner = new CommandRunner(); + } + + start(): Promise + { + const commandName = this.#argumentProcessor.getCommand() ?? 'help'; + + return this.#commandRunner.run(commandName, this.#argumentProcessor); + } +} diff --git a/packages/cli/src/Command.ts b/packages/cli/src/Command.ts new file mode 100644 index 00000000..6d27edad --- /dev/null +++ b/packages/cli/src/Command.ts @@ -0,0 +1,9 @@ + +import type ArgumentProcessor from './ArgumentProcessor'; + +interface Command +{ + execute(args: ArgumentProcessor): Promise; +} + +export default Command; diff --git a/packages/cli/src/CommandRunner.ts b/packages/cli/src/CommandRunner.ts new file mode 100644 index 00000000..b3f2bfac --- /dev/null +++ b/packages/cli/src/CommandRunner.ts @@ -0,0 +1,37 @@ + +import Command from './Command'; + +import ShowHelp from './commands/ShowHelp'; +import ShowAbout from './commands/ShowAbout'; +import ShowVersion from './commands/ShowVersion'; +import BuildApp from './commands/BuildApp'; +import StartServer from './commands/StartServer'; + +import ArgumentManager from './ArgumentProcessor'; + +export default class CommandRunner +{ + #commands: Map = new Map(); + + constructor() + { + // TODO: add 'init' command + this.#commands.set('help', new ShowHelp()); + this.#commands.set('about', new ShowAbout()); + this.#commands.set('version', new ShowVersion()); + this.#commands.set('build', new BuildApp()); + this.#commands.set('start', new StartServer()); + } + + run(name: string, args: ArgumentManager): Promise + { + const command = this.#commands.get(name); + + if (command === undefined) + { + throw new Error(`Command ${name} not found`); + } + + return command.execute(args); + } +} diff --git a/packages/cli/src/commands/BuildApp.ts b/packages/cli/src/commands/BuildApp.ts new file mode 100644 index 00000000..710a171c --- /dev/null +++ b/packages/cli/src/commands/BuildApp.ts @@ -0,0 +1,25 @@ + +import { ConfigurationManager } from '@jitar/configuration'; +import { BuildManager } from '@jitar/build'; + +import Command from '../Command'; +import ArgumentProcessor from '../ArgumentProcessor'; + +export default class BuildApp implements Command +{ + async execute(args: ArgumentProcessor): Promise + { + const environmentFile = args.getOptionalArgument('--env-file', undefined); + const runtimeConfigFile = args.getOptionalArgument('--config', undefined); + + const configurationManager = new ConfigurationManager(); + + await configurationManager.configureEnvironment(environmentFile); + + const configuration = await configurationManager.getRuntimeConfiguration(runtimeConfigFile); + + const buildManager = new BuildManager(configuration); + + return buildManager.build(); + } +} diff --git a/packages/cli/src/commands/ShowAbout.ts b/packages/cli/src/commands/ShowAbout.ts new file mode 100644 index 00000000..16764297 --- /dev/null +++ b/packages/cli/src/commands/ShowAbout.ts @@ -0,0 +1,20 @@ + +import Command from '../Command'; +import ArgumentProcessor from '../ArgumentProcessor'; + +const information = ` +Jitar is a JavaScript Distributed Runtime created and maintained by Masking Technology. + +More information can be found at: +- https://jitar.dev +- https://masking.tech +`; + +export default class ShowAbout implements Command +{ + // eslint-disable-next-line @typescript-eslint/no-unused-vars + async execute(args: ArgumentProcessor): Promise + { + console.log(information); + } +} diff --git a/packages/cli/src/commands/ShowHelp.ts b/packages/cli/src/commands/ShowHelp.ts new file mode 100644 index 00000000..52240609 --- /dev/null +++ b/packages/cli/src/commands/ShowHelp.ts @@ -0,0 +1,30 @@ + +import Command from '../Command'; +import ArgumentProcessor from '../ArgumentProcessor'; + +const message = ` +Usage: jitar [options] + +Commands: + build Builds the application (creates segment bundles) + start Starts a server with the configured service + about Shows information about Jitar + version Shows the installed version of Jitar + help Shows help (this message) + +Options: + --config Path to the configuration file (default: jitar.json) + --service Path to the service configuration file (required for 'start' command) + --env-file Path to the environment file (default: none) + +More information can be found at https://docs.jitar.dev +`; + +export default class ShowHelp implements Command +{ + // eslint-disable-next-line @typescript-eslint/no-unused-vars + async execute(args: ArgumentProcessor): Promise + { + console.log(message); + } +} diff --git a/packages/cli/src/commands/ShowVersion.ts b/packages/cli/src/commands/ShowVersion.ts new file mode 100644 index 00000000..b81ae1ef --- /dev/null +++ b/packages/cli/src/commands/ShowVersion.ts @@ -0,0 +1,14 @@ + +import Command from '../Command'; +import ArgumentProcessor from '../ArgumentProcessor'; + +const versionNumber = `v0.8.0`; + +export default class ShowVersion implements Command +{ + // eslint-disable-next-line @typescript-eslint/no-unused-vars + async execute(args: ArgumentProcessor): Promise + { + console.log(versionNumber); + } +} diff --git a/packages/cli/src/commands/StartServer.ts b/packages/cli/src/commands/StartServer.ts new file mode 100644 index 00000000..49490682 --- /dev/null +++ b/packages/cli/src/commands/StartServer.ts @@ -0,0 +1,67 @@ + +import { ConfigurationManager, RuntimeConfiguration, ServerConfiguration } from '@jitar/configuration'; +import { LocalFileManager, SourcingManager } from '@jitar/sourcing'; +import { ServerBuilder } from '@jitar/runtime'; +import { HttpServer, HttpRemoteBuilder } from '@jitar/http'; + +import Command from '../Command'; +import ArgumentProcessor from '../ArgumentProcessor'; + +const banner = ` + ██╗██╗████████╗ █████╗ ██████╗ + ██║██║╚══██╔══╝██╔══██╗██╔══██╗ + ██║██║ ██║ ███████║██████╔╝ +██ ██║██║ ██║ ██╔══██║██╔══██╗ +╚█████╔╝██║ ██║ ██║ ██║██║ ██║ + ╚════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝ + ~ Distributed JavaScript Runtime ~ +____________________________________ +`; + +export default class StartServer implements Command +{ + async execute(args: ArgumentProcessor): Promise + { + const environmentFile = args.getOptionalArgument('--env-file', undefined); + const runtimeConfigFile = args.getOptionalArgument('--config', undefined); + const serviceConfigFile = args.getRequiredArgument('--service'); + + const bodyLimitString = args.getOptionalArgument('--http-body-limit', undefined); + const bodyLimit = bodyLimitString !== undefined ? Number.parseInt(bodyLimitString) : undefined; + + const configurationManager = new ConfigurationManager(); + + await configurationManager.configureEnvironment(environmentFile); + + const runtimeConfiguration = await configurationManager.getRuntimeConfiguration(runtimeConfigFile); + const serverConfiguration = await configurationManager.getServerConfiguration(serviceConfigFile); + + const httpServer = await this.#buildServer(runtimeConfiguration, serverConfiguration, bodyLimit); + + return this.#runServer(httpServer); + } + + async #buildServer(runtimeConfiguration: RuntimeConfiguration, serverConfiguration: ServerConfiguration, bodyLimit?: number): Promise + { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const [protocol, host, port] = serverConfiguration.url.split(':'); + + const fileManager = new LocalFileManager(runtimeConfiguration.target); + const sourcingManager = new SourcingManager(fileManager); + const remoteBuilder = new HttpRemoteBuilder(); + const serverBuilder = new ServerBuilder(sourcingManager, remoteBuilder); + + const server = await serverBuilder.build(serverConfiguration); + + return new HttpServer(server, port, bodyLimit); + } + + #runServer(httpServer: HttpServer): Promise + { + process.on('SIGINT', async () => httpServer.stop()); + + console.log(banner); + + return httpServer.start(); + } +} diff --git a/packages/cli/src/errors/MissingArgument.ts b/packages/cli/src/errors/MissingArgument.ts new file mode 100644 index 00000000..fdf2b8b2 --- /dev/null +++ b/packages/cli/src/errors/MissingArgument.ts @@ -0,0 +1,8 @@ + +export default class MissingArgument extends Error +{ + constructor(name: string) + { + super(`Missing argument '${name}'`); + } +} diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts new file mode 100644 index 00000000..b1e8d4d0 --- /dev/null +++ b/packages/cli/src/index.ts @@ -0,0 +1,2 @@ + +export { default as Cli } from './Cli'; diff --git a/packages/cli/test/ArgumentProcessor.spec.ts b/packages/cli/test/ArgumentProcessor.spec.ts new file mode 100644 index 00000000..f2c26f01 --- /dev/null +++ b/packages/cli/test/ArgumentProcessor.spec.ts @@ -0,0 +1,53 @@ + +import { describe, expect, it } from 'vitest'; + +import ArgumentProcessor from '../src/ArgumentProcessor'; + +import { ARGUMENTS } from './fixtures'; + +const processor = new ArgumentProcessor(ARGUMENTS); + +describe('ArgumentProcessor', () => +{ + it('should get third argument as command', () => + { + const commandName = processor.getCommand(); + + expect(commandName).toEqual('commandName'); + }); + + it('should return required argument value', () => + { + const value = processor.getRequiredArgument('first'); + + expect(value).toEqual('1'); + }); + + it('should throw error when required argument value is missing', () => + { + const run = () => processor.getRequiredArgument('second'); + + expect(run).toThrow("Missing argument 'second'"); + }); + + it('should throw error when required argument key is missing', () => + { + const run = () => processor.getRequiredArgument('third'); + + expect(run).toThrow("Missing argument 'third'"); + }); + + it('should return default value if optional value is missing', () => + { + const value = processor.getOptionalArgument('second', 'default'); + + expect(value).toEqual('default'); + }); + + it('should return default value if optional key is missing', () => + { + const value = processor.getOptionalArgument('third', 'default'); + + expect(value).toEqual('default'); + }); +}); diff --git a/packages/cli/test/fixtures/arguments.fixture.ts b/packages/cli/test/fixtures/arguments.fixture.ts new file mode 100644 index 00000000..8da9ad2a --- /dev/null +++ b/packages/cli/test/fixtures/arguments.fixture.ts @@ -0,0 +1,2 @@ + +export const ARGUMENTS = ['node', 'script', 'commandName', 'first=1', 'second']; diff --git a/packages/cli/test/fixtures/index.ts b/packages/cli/test/fixtures/index.ts new file mode 100644 index 00000000..1143b171 --- /dev/null +++ b/packages/cli/test/fixtures/index.ts @@ -0,0 +1,2 @@ + +export * from './arguments.fixture'; diff --git a/examples/concepts/overrides/tsconfig.json b/packages/cli/tsconfig.json similarity index 79% rename from examples/concepts/overrides/tsconfig.json rename to packages/cli/tsconfig.json index 9d1aec07..4ca2247d 100644 --- a/examples/concepts/overrides/tsconfig.json +++ b/packages/cli/tsconfig.json @@ -4,16 +4,18 @@ "module": "es2022", "rootDir": "./src/", "moduleResolution": "node", + "declaration": true, "outDir": "./dist", "removeComments": true, + "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "strict": true, "skipLibCheck": true }, "exclude": [ - "cache", - "dist", + "vite.config.ts", "node_modules", - "segments" + "dist", + "test" ] } \ No newline at end of file diff --git a/packages/cli/vite.config.ts b/packages/cli/vite.config.ts new file mode 100644 index 00000000..f5182e1f --- /dev/null +++ b/packages/cli/vite.config.ts @@ -0,0 +1,10 @@ +// vite.config.ts +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + coverage: { + provider: 'v8' + }, + }, +}); diff --git a/packages/configuration/CHANGELOG.md b/packages/configuration/CHANGELOG.md new file mode 100644 index 00000000..80f29432 --- /dev/null +++ b/packages/configuration/CHANGELOG.md @@ -0,0 +1,4 @@ + +# Changelog + +This package doesn't keep a changelog. See the changelog in the [github repository](https://github.com/MaskingTechnology/jitar/blob/main/CHANGELOG.md) \ No newline at end of file diff --git a/packages/configuration/README.md b/packages/configuration/README.md new file mode 100644 index 00000000..fc1f6295 --- /dev/null +++ b/packages/configuration/README.md @@ -0,0 +1,9 @@ + +# Jitar Configuration + +This package provides the configurations for the [Jitar](https://jitar.dev) runtime. + +For more information about Jitar: + +* [Visit our website](https://jitar.dev) +* [Read the documentation](https://docs.jitar.dev). diff --git a/packages/configuration/package.json b/packages/configuration/package.json new file mode 100644 index 00000000..a59ded7b --- /dev/null +++ b/packages/configuration/package.json @@ -0,0 +1,23 @@ +{ + "name": "@jitar/configuration", + "version": "0.7.4", + "description": "Configuration library for the Jitar runtime.", + "author": "Masking Technology (https://jitar.dev)", + "license": "MIT", + "type": "module", + "types": "dist/index.d.ts", + "exports": "./dist/index.js", + "private": true, + "scripts": { + "test": "vitest run", + "test-coverage": "vitest run --coverage", + "lint": "eslint . --ext .ts", + "build": "tsc -p tsconfig.json", + "clean": "rm -rf dist" + }, + "dependencies": { + "@jitar/sourcing": "*", + "@jitar/validation": "*", + "dotenv": "^16.4.5" + } +} diff --git a/packages/configuration/src/ConfigurationManager.ts b/packages/configuration/src/ConfigurationManager.ts new file mode 100644 index 00000000..0a945e14 --- /dev/null +++ b/packages/configuration/src/ConfigurationManager.ts @@ -0,0 +1,42 @@ + +import { Validator } from '@jitar/validation'; + +import { EnvironmentConfigurator } from './environment'; +import { RuntimeConfiguration, RuntimeConfigurationBuilder } from './runtime'; +import { ServerConfiguration, ServerConfigurationBuilder } from './server'; +import { ConfigurationReader } from './utils'; + +const DEFAULT_ROOT_PATH = './'; +const DEFAULT_ENVIRONMENT_FILE = '.env'; + +export default class ConfigurationManager +{ + #environmentConfigurator: EnvironmentConfigurator; + #runtimeConfigurationBuilder: RuntimeConfigurationBuilder; + #serverConfigurationBuilder : ServerConfigurationBuilder; + + constructor(rootPath: string = DEFAULT_ROOT_PATH) + { + const reader = new ConfigurationReader(rootPath); + const validator = new Validator(); + + this.#environmentConfigurator = new EnvironmentConfigurator(); + this.#runtimeConfigurationBuilder = new RuntimeConfigurationBuilder(reader, validator); + this.#serverConfigurationBuilder = new ServerConfigurationBuilder(reader, validator); + } + + configureEnvironment(filename: string = DEFAULT_ENVIRONMENT_FILE): Promise + { + return this.#environmentConfigurator.configure(filename); + } + + getRuntimeConfiguration(filename?: string): Promise + { + return this.#runtimeConfigurationBuilder.build(filename); + } + + getServerConfiguration(filename: string): Promise + { + return this.#serverConfigurationBuilder.build(filename); + } +} diff --git a/packages/configuration/src/environment/Configurator.ts b/packages/configuration/src/environment/Configurator.ts new file mode 100644 index 00000000..319769ba --- /dev/null +++ b/packages/configuration/src/environment/Configurator.ts @@ -0,0 +1,10 @@ + +import dotenv from 'dotenv'; + +export default class Configurator +{ + async configure(filename: string): Promise + { + dotenv.config({ path: filename }); + } +} diff --git a/packages/configuration/src/environment/index.ts b/packages/configuration/src/environment/index.ts new file mode 100644 index 00000000..2e718f44 --- /dev/null +++ b/packages/configuration/src/environment/index.ts @@ -0,0 +1,2 @@ + +export { default as EnvironmentConfigurator } from './Configurator'; diff --git a/packages/configuration/src/index.ts b/packages/configuration/src/index.ts new file mode 100644 index 00000000..401ed95f --- /dev/null +++ b/packages/configuration/src/index.ts @@ -0,0 +1,5 @@ + +export { RuntimeConfiguration } from './runtime'; +export { ServerConfiguration, StandaloneConfiguration, ProxyConfiguration, WorkerConfiguration, GatewayConfiguration, RepositoryConfiguration } from './server'; + +export { default as ConfigurationManager } from './ConfigurationManager'; diff --git a/packages/configuration/src/runtime/ConfigurationBuilder.ts b/packages/configuration/src/runtime/ConfigurationBuilder.ts new file mode 100644 index 00000000..d906d5b6 --- /dev/null +++ b/packages/configuration/src/runtime/ConfigurationBuilder.ts @@ -0,0 +1,35 @@ + +import type { Validator } from '@jitar/validation'; + +import type { ConfigurationReader } from '../utils'; + +import RuntimeConfiguration, { DefaultValues, validationScheme } from './definitions/RuntimeConfiguration'; +import RuntimeConfigurationInvalid from './errors/RuntimeConfigurationInvalid'; + +export default class ConfigurationBuilder +{ + #reader: ConfigurationReader; + #validator: Validator; + + constructor(reader: ConfigurationReader, validator: Validator) + { + this.#reader = reader; + this.#validator = validator; + } + + async build(filename: string = DefaultValues.FILENAME): Promise + { + const configuration = await this.#reader.read(filename) as RuntimeConfiguration; + const validation = this.#validator.validate(configuration, validationScheme); + + if (validation.valid === false) + { + throw new RuntimeConfigurationInvalid(validation); + } + + configuration.source ??= DefaultValues.SOURCE; + configuration.target ??= DefaultValues.TARGET; + + return configuration; + } +} diff --git a/packages/configuration/src/runtime/definitions/RuntimeConfiguration.ts b/packages/configuration/src/runtime/definitions/RuntimeConfiguration.ts new file mode 100644 index 00000000..a44fe738 --- /dev/null +++ b/packages/configuration/src/runtime/definitions/RuntimeConfiguration.ts @@ -0,0 +1,25 @@ + +import type { ValidationScheme } from '@jitar/validation'; + +type RuntimeConfiguration = +{ + source: string; + target: string; +}; + +export default RuntimeConfiguration; + +const DefaultValues = +{ + FILENAME: './jitar.json', + SOURCE: './src', + TARGET: './dist' +} as const; + +const validationScheme: ValidationScheme = +{ + source: { type: 'string', required: false }, + target: { type: 'string', required: false } +} as const; + +export { DefaultValues, validationScheme }; diff --git a/packages/configuration/src/runtime/errors/RuntimeConfigurationInvalid.ts b/packages/configuration/src/runtime/errors/RuntimeConfigurationInvalid.ts new file mode 100644 index 00000000..c1011b28 --- /dev/null +++ b/packages/configuration/src/runtime/errors/RuntimeConfigurationInvalid.ts @@ -0,0 +1,12 @@ + +import type { ValidationResult } from '@jitar/validation'; + +export default class RuntimeConfigurationInvalid extends Error +{ + public constructor(validation: ValidationResult) + { + const errorMessages = validation.errors.join('\n'); + + super(`Runtime configuration is invalid:\n${errorMessages}`); + } +} diff --git a/packages/configuration/src/runtime/index.ts b/packages/configuration/src/runtime/index.ts new file mode 100644 index 00000000..19271b5c --- /dev/null +++ b/packages/configuration/src/runtime/index.ts @@ -0,0 +1,3 @@ + +export { default as RuntimeConfiguration } from './definitions/RuntimeConfiguration'; +export { default as RuntimeConfigurationBuilder } from './ConfigurationBuilder'; diff --git a/packages/configuration/src/server/ConfigurationBuilder.ts b/packages/configuration/src/server/ConfigurationBuilder.ts new file mode 100644 index 00000000..5469433e --- /dev/null +++ b/packages/configuration/src/server/ConfigurationBuilder.ts @@ -0,0 +1,32 @@ + +import type { Validator } from '@jitar/validation'; + +import type { ConfigurationReader } from '../utils'; + +import ServerConfiguration, { validationScheme } from './definitions/ServerConfiguration'; +import ServerConfigurationInvalid from './errors/ServerConfigurationInvalid'; + +export default class ConfigurationBuilder +{ + #reader: ConfigurationReader; + #validator: Validator; + + constructor(reader: ConfigurationReader, validator: Validator) + { + this.#reader = reader; + this.#validator = validator; + } + + async build(filename: string): Promise + { + const configuration = await this.#reader.read(filename) as ServerConfiguration; + const validation = this.#validator.validate(configuration, validationScheme); + + if (validation.valid === false) + { + throw new ServerConfigurationInvalid(validation); + } + + return configuration; + } +} diff --git a/packages/configuration/src/server/definitions/GatewayConfiguration.ts b/packages/configuration/src/server/definitions/GatewayConfiguration.ts new file mode 100644 index 00000000..67747eff --- /dev/null +++ b/packages/configuration/src/server/definitions/GatewayConfiguration.ts @@ -0,0 +1,18 @@ + +import type { ValidationScheme } from '@jitar/validation'; + +type GatewayConfiguration = +{ + monitor: number; + trustKey?: string; +}; + +export default GatewayConfiguration; + +const validationScheme: ValidationScheme = +{ + monitor: { type: 'integer', required: false }, + trustKey: { type: 'string', required: false } +} as const; + +export { validationScheme }; diff --git a/packages/configuration/src/server/definitions/ProxyConfiguration.ts b/packages/configuration/src/server/definitions/ProxyConfiguration.ts new file mode 100644 index 00000000..40f723e7 --- /dev/null +++ b/packages/configuration/src/server/definitions/ProxyConfiguration.ts @@ -0,0 +1,18 @@ + +import type { ValidationScheme } from '@jitar/validation'; + +type ProxyConfiguration = +{ + gateway: string; + repository: string; +}; + +export default ProxyConfiguration; + +const validationScheme: ValidationScheme = +{ + gateway: { type: 'url', required: true }, + repository: { type: 'url', required: true } +} as const; + +export { validationScheme }; diff --git a/packages/configuration/src/server/definitions/RepositoryConfiguration.ts b/packages/configuration/src/server/definitions/RepositoryConfiguration.ts new file mode 100644 index 00000000..955f5e7e --- /dev/null +++ b/packages/configuration/src/server/definitions/RepositoryConfiguration.ts @@ -0,0 +1,20 @@ + +import type { ValidationScheme } from '@jitar/validation'; + +type RepositoryConfiguration = +{ + indexFilename?: string; + serveIndexOnNotFound?: boolean; + assets?: string[]; +}; + +export default RepositoryConfiguration; + +const validationScheme: ValidationScheme = +{ + indexFilename: { type: 'string', required: false }, + serveIndexOnNotFound: { type: 'boolean', required: false }, + assets: { type: 'list', required: false, items: { type: 'string' } } +} as const; + +export { validationScheme }; diff --git a/packages/configuration/src/server/definitions/ServerConfiguration.ts b/packages/configuration/src/server/definitions/ServerConfiguration.ts new file mode 100644 index 00000000..588c0bb7 --- /dev/null +++ b/packages/configuration/src/server/definitions/ServerConfiguration.ts @@ -0,0 +1,42 @@ + +import type { ValidationScheme } from '@jitar/validation'; + +import GatewayConfiguration, { validationScheme as gatewayValidationScheme } from './GatewayConfiguration'; +import ProxyConfiguration, { validationScheme as proxyValidationScheme } from './ProxyConfiguration'; +import RepositoryConfiguration, { validationScheme as repositoryValidationScheme } from './RepositoryConfiguration'; +import StandaloneConfiguration, { validationScheme as standaloneValidationScheme } from './StandaloneConfiguration'; +import WorkerConfiguration, { validationScheme as workerValidationScheme } from './WorkerConfiguration'; + +type ServerConfiguration = +{ + url: string; + setUp?: string[]; + tearDown?: string[]; + middleware?: string[]; + healthChecks?: string[]; + + gateway?: GatewayConfiguration; + proxy?: ProxyConfiguration; + repository?: RepositoryConfiguration; + standalone?: StandaloneConfiguration; + worker?: WorkerConfiguration; +}; + +export default ServerConfiguration; + +const validationScheme: ValidationScheme = +{ + url: { type: 'url', required: true }, + setUp: { type: 'list', required: false, items: { type: 'string' } }, + tearDown: { type: 'list', required: false, items: { type: 'string' } }, + middleware: { type: 'list', required: false, items: { type: 'string' } }, + healthChecks: { type: 'list', required: false, items: { type: 'string' } }, + + gateway: { type: 'group', required: false, fields: gatewayValidationScheme }, + proxy: { type: 'group', required: false, fields: proxyValidationScheme }, + repository: { type: 'group', required: false, fields: repositoryValidationScheme }, + standalone: { type: 'group', required: false, fields: standaloneValidationScheme }, + worker: { type: 'group', required: false, fields: workerValidationScheme } +} as const; + +export { validationScheme }; diff --git a/packages/configuration/src/server/definitions/StandaloneConfiguration.ts b/packages/configuration/src/server/definitions/StandaloneConfiguration.ts new file mode 100644 index 00000000..fb9ffcfc --- /dev/null +++ b/packages/configuration/src/server/definitions/StandaloneConfiguration.ts @@ -0,0 +1,22 @@ + +import type { ValidationScheme } from '@jitar/validation'; + +type StandaloneConfiguration = +{ + segments: string[]; + indexFilename?: string; + serveIndexOnNotFound?: boolean; + assets?: string[]; +}; + +export default StandaloneConfiguration; + +const validationScheme: ValidationScheme = +{ + segments: { type: 'list', required: true, items: { type: 'string' } }, + indexFilename: { type: 'string', required: false }, + serveIndexOnNotFound: { type: 'boolean', required: false }, + assets: { type: 'list', required: false, items: { type: 'string' } } +} as const; + +export { validationScheme }; diff --git a/packages/configuration/src/server/definitions/WorkerConfiguration.ts b/packages/configuration/src/server/definitions/WorkerConfiguration.ts new file mode 100644 index 00000000..a9298446 --- /dev/null +++ b/packages/configuration/src/server/definitions/WorkerConfiguration.ts @@ -0,0 +1,20 @@ + +import type { ValidationScheme } from '@jitar/validation'; + +type WorkerConfiguration = +{ + gateway?: string; + segments: string[]; + trustKey?: string; +}; + +export default WorkerConfiguration; + +const validationScheme: ValidationScheme = +{ + gateway: { type: 'url', required: false }, + segments: { type: 'list', required: true, items: { type: 'string' } }, + trustKey: { type: 'string', required: false } +} as const; + +export { validationScheme }; diff --git a/packages/configuration/src/server/errors/ServerConfigurationInvalid.ts b/packages/configuration/src/server/errors/ServerConfigurationInvalid.ts new file mode 100644 index 00000000..aff83195 --- /dev/null +++ b/packages/configuration/src/server/errors/ServerConfigurationInvalid.ts @@ -0,0 +1,14 @@ + +import type { ValidationResult } from '@jitar/validation'; + +const BREAK = '\n => '; + +export default class ServerConfigurationInvalid extends Error +{ + public constructor(validation: ValidationResult) + { + const errorMessages = validation.errors.join(BREAK); + + super(`Invalid server configuration:${BREAK}${errorMessages}`); + } +} diff --git a/packages/configuration/src/server/index.ts b/packages/configuration/src/server/index.ts new file mode 100644 index 00000000..e93287f3 --- /dev/null +++ b/packages/configuration/src/server/index.ts @@ -0,0 +1,9 @@ + +export { default as ServerConfiguration } from './definitions/ServerConfiguration'; +export { default as StandaloneConfiguration } from './definitions/StandaloneConfiguration'; +export { default as ProxyConfiguration } from './definitions/ProxyConfiguration'; +export { default as WorkerConfiguration } from './definitions/WorkerConfiguration'; +export { default as GatewayConfiguration } from './definitions/GatewayConfiguration'; +export { default as RepositoryConfiguration } from './definitions/RepositoryConfiguration'; + +export { default as ServerConfigurationBuilder } from './ConfigurationBuilder'; diff --git a/packages/configuration/src/utils/ConfigurationReader.ts b/packages/configuration/src/utils/ConfigurationReader.ts new file mode 100644 index 00000000..1941ff4f --- /dev/null +++ b/packages/configuration/src/utils/ConfigurationReader.ts @@ -0,0 +1,52 @@ + +import { LocalFileManager } from '@jitar/sourcing'; +import type { FileManager } from '@jitar/sourcing'; + +const ENVIRONMENT_VARIABLE_REGEX = /\${([^}]*)}/g; + +export default class ConfigurationReader +{ + #fileManager: FileManager; + + constructor(rootPath: string) + { + this.#fileManager = new LocalFileManager(rootPath); + } + + async read(filename: string): Promise> + { + const fileExists = await this.#fileManager.exists(filename); + + if (fileExists === false) + { + return {}; + } + + const file = await this.#fileManager.read(filename); + const content = file.content.toString(); + const configuration = this.#replaceEnvironmentVariables(content); + + return file.type.includes('json') + ? this.#parseJson(configuration) + : this.#parseText(configuration); + } + + #replaceEnvironmentVariables(content: string): string + { + return content.replace(ENVIRONMENT_VARIABLE_REGEX, (match, key) => + { + return process.env[key] ?? 'null'; + }); + } + + #parseJson(configuration: string): Record + { + return JSON.parse(configuration); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + #parseText(configuration: string): Record + { + return {}; + } +} diff --git a/packages/configuration/src/utils/index.ts b/packages/configuration/src/utils/index.ts new file mode 100644 index 00000000..7df7adc6 --- /dev/null +++ b/packages/configuration/src/utils/index.ts @@ -0,0 +1,2 @@ + +export { default as ConfigurationReader } from './ConfigurationReader'; diff --git a/packages/configuration/test/dummy.spec.ts b/packages/configuration/test/dummy.spec.ts new file mode 100644 index 00000000..2489f1ee --- /dev/null +++ b/packages/configuration/test/dummy.spec.ts @@ -0,0 +1,12 @@ + +import { describe, expect, it } from 'vitest'; + +describe('dummy', () => +{ + // TODO: Add real tests + + it('should not complain about missing tests', async () => + { + expect(true).toBeTruthy(); + }); +}); diff --git a/packages/server-nodejs/tsconfig.json b/packages/configuration/tsconfig.json similarity index 92% rename from packages/server-nodejs/tsconfig.json rename to packages/configuration/tsconfig.json index e803accf..e66fac12 100644 --- a/packages/server-nodejs/tsconfig.json +++ b/packages/configuration/tsconfig.json @@ -13,8 +13,9 @@ "skipLibCheck": true }, "exclude": [ + "vite.config.ts", "node_modules", - "test", "dist", + "test" ] } \ No newline at end of file diff --git a/packages/configuration/vite.config.ts b/packages/configuration/vite.config.ts new file mode 100644 index 00000000..f5182e1f --- /dev/null +++ b/packages/configuration/vite.config.ts @@ -0,0 +1,10 @@ +// vite.config.ts +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + coverage: { + provider: 'v8' + }, + }, +}); diff --git a/packages/errors/CHANGELOG.md b/packages/errors/CHANGELOG.md new file mode 100644 index 00000000..80f29432 --- /dev/null +++ b/packages/errors/CHANGELOG.md @@ -0,0 +1,4 @@ + +# Changelog + +This package doesn't keep a changelog. See the changelog in the [github repository](https://github.com/MaskingTechnology/jitar/blob/main/CHANGELOG.md) \ No newline at end of file diff --git a/packages/errors/README.md b/packages/errors/README.md new file mode 100644 index 00000000..29b490f2 --- /dev/null +++ b/packages/errors/README.md @@ -0,0 +1,9 @@ + +# Jitar Errors + +This package provides the generic errors thrown by the [Jitar](https://jitar.dev) runtime. + +For more information about Jitar: + +* [Visit our website](https://jitar.dev) +* [Read the documentation](https://docs.jitar.dev). diff --git a/packages/errors/package.json b/packages/errors/package.json new file mode 100644 index 00000000..898c2fe2 --- /dev/null +++ b/packages/errors/package.json @@ -0,0 +1,17 @@ +{ + "name": "@jitar/errors", + "version": "0.7.4", + "description": "Error library for the Jitar runtime.", + "author": "Masking Technology (https://jitar.dev)", + "license": "MIT", + "type": "module", + "types": "dist/index.d.ts", + "exports": "./dist/index.js", + "private": true, + "scripts": { + "test": "", + "lint": "eslint . --ext .ts", + "build": "tsc -p tsconfig.json", + "clean": "rm -rf dist" + } +} diff --git a/packages/runtime/src/errors/generic/BadRequest.ts b/packages/errors/src/BadRequest.ts similarity index 54% rename from packages/runtime/src/errors/generic/BadRequest.ts rename to packages/errors/src/BadRequest.ts index c805bd78..a11dc779 100644 --- a/packages/runtime/src/errors/generic/BadRequest.ts +++ b/packages/errors/src/BadRequest.ts @@ -1,6 +1,4 @@ -import { Loadable } from '@jitar/serialization'; - export default class BadRequest extends Error { constructor(message = 'Invalid request') @@ -8,5 +6,3 @@ export default class BadRequest extends Error super(message); } } - -(BadRequest as Loadable).source = 'RUNTIME_ERROR_LOCATION'; diff --git a/packages/runtime/src/errors/generic/Forbidden.ts b/packages/errors/src/Forbidden.ts similarity index 53% rename from packages/runtime/src/errors/generic/Forbidden.ts rename to packages/errors/src/Forbidden.ts index 75148f0e..d3b1afc8 100644 --- a/packages/runtime/src/errors/generic/Forbidden.ts +++ b/packages/errors/src/Forbidden.ts @@ -1,6 +1,4 @@ -import { Loadable } from '@jitar/serialization'; - export default class Forbidden extends Error { constructor(message = 'Forbidden') @@ -8,5 +6,3 @@ export default class Forbidden extends Error super(message); } } - -(Forbidden as Loadable).source = 'RUNTIME_ERROR_LOCATION'; diff --git a/packages/runtime/src/errors/generic/NotFound.ts b/packages/errors/src/NotFound.ts similarity index 53% rename from packages/runtime/src/errors/generic/NotFound.ts rename to packages/errors/src/NotFound.ts index b1987129..95a67096 100644 --- a/packages/runtime/src/errors/generic/NotFound.ts +++ b/packages/errors/src/NotFound.ts @@ -1,6 +1,4 @@ -import { Loadable } from '@jitar/serialization'; - export default class NotFound extends Error { constructor(message = 'Not found') @@ -8,5 +6,3 @@ export default class NotFound extends Error super(message); } } - -(NotFound as Loadable).source = 'RUNTIME_ERROR_LOCATION'; diff --git a/packages/runtime/src/errors/generic/NotImplemented.ts b/packages/errors/src/NotImplemented.ts similarity index 54% rename from packages/runtime/src/errors/generic/NotImplemented.ts rename to packages/errors/src/NotImplemented.ts index e6397fa8..fa59ac18 100644 --- a/packages/runtime/src/errors/generic/NotImplemented.ts +++ b/packages/errors/src/NotImplemented.ts @@ -1,6 +1,4 @@ -import { Loadable } from '@jitar/serialization'; - export default class NotImplemented extends Error { constructor(message = 'Not implemented') @@ -8,5 +6,3 @@ export default class NotImplemented extends Error super(message); } } - -(NotImplemented as Loadable).source = 'RUNTIME_ERROR_LOCATION'; diff --git a/packages/runtime/src/errors/generic/PaymentRequired.ts b/packages/errors/src/PaymentRequired.ts similarity index 54% rename from packages/runtime/src/errors/generic/PaymentRequired.ts rename to packages/errors/src/PaymentRequired.ts index 42243c25..dc0d2ade 100644 --- a/packages/runtime/src/errors/generic/PaymentRequired.ts +++ b/packages/errors/src/PaymentRequired.ts @@ -1,6 +1,4 @@ -import { Loadable } from '@jitar/serialization'; - export default class PaymentRequired extends Error { constructor(message = 'Payment required') @@ -8,5 +6,3 @@ export default class PaymentRequired extends Error super(message); } } - -(PaymentRequired as Loadable).source = 'RUNTIME_ERROR_LOCATION'; diff --git a/packages/runtime/src/errors/generic/ServerError.ts b/packages/errors/src/ServerError.ts similarity index 53% rename from packages/runtime/src/errors/generic/ServerError.ts rename to packages/errors/src/ServerError.ts index 79c6bbfc..c695c01f 100644 --- a/packages/runtime/src/errors/generic/ServerError.ts +++ b/packages/errors/src/ServerError.ts @@ -1,6 +1,4 @@ -import { Loadable } from '@jitar/serialization'; - export default class ServerError extends Error { constructor(message = 'Server error') @@ -8,5 +6,3 @@ export default class ServerError extends Error super(message); } } - -(ServerError as Loadable).source = 'RUNTIME_ERROR_LOCATION'; diff --git a/packages/errors/src/Teapot.ts b/packages/errors/src/Teapot.ts new file mode 100644 index 00000000..2dce3cd6 --- /dev/null +++ b/packages/errors/src/Teapot.ts @@ -0,0 +1,8 @@ + +export default class Teapot extends Error +{ + constructor(message = "I'm a teapot") + { + super(message); + } +} diff --git a/packages/runtime/src/errors/generic/Unauthorized.ts b/packages/errors/src/Unauthorized.ts similarity index 53% rename from packages/runtime/src/errors/generic/Unauthorized.ts rename to packages/errors/src/Unauthorized.ts index 99161323..f33d56e8 100644 --- a/packages/runtime/src/errors/generic/Unauthorized.ts +++ b/packages/errors/src/Unauthorized.ts @@ -1,6 +1,4 @@ -import { Loadable } from '@jitar/serialization'; - export default class Unauthorized extends Error { constructor(message = 'Unauthorized') @@ -8,5 +6,3 @@ export default class Unauthorized extends Error super(message); } } - -(Unauthorized as Loadable).source = 'RUNTIME_ERROR_LOCATION'; diff --git a/packages/errors/src/index.ts b/packages/errors/src/index.ts new file mode 100644 index 00000000..17bca636 --- /dev/null +++ b/packages/errors/src/index.ts @@ -0,0 +1,9 @@ + +export { default as BadRequest } from './BadRequest'; +export { default as Forbidden } from './Forbidden'; +export { default as NotFound } from './NotFound'; +export { default as NotImplemented } from './NotImplemented'; +export { default as PaymentRequired } from './PaymentRequired'; +export { default as ServerError } from './ServerError'; +export { default as Teapot } from './Teapot'; +export { default as Unauthorized } from './Unauthorized'; diff --git a/packages/errors/tsconfig.json b/packages/errors/tsconfig.json new file mode 100644 index 00000000..e66fac12 --- /dev/null +++ b/packages/errors/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "rootDir": "./src/", + "moduleResolution": "node", + "declaration": true, + "outDir": "./dist", + "removeComments": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "skipLibCheck": true + }, + "exclude": [ + "vite.config.ts", + "node_modules", + "dist", + "test" + ] +} \ No newline at end of file diff --git a/packages/execution/CHANGELOG.md b/packages/execution/CHANGELOG.md new file mode 100644 index 00000000..80f29432 --- /dev/null +++ b/packages/execution/CHANGELOG.md @@ -0,0 +1,4 @@ + +# Changelog + +This package doesn't keep a changelog. See the changelog in the [github repository](https://github.com/MaskingTechnology/jitar/blob/main/CHANGELOG.md) \ No newline at end of file diff --git a/packages/execution/README.md b/packages/execution/README.md new file mode 100644 index 00000000..297f0b60 --- /dev/null +++ b/packages/execution/README.md @@ -0,0 +1,9 @@ + +# Jitar Execution + +This package provides the execution model (segmentation, procedure, etc.) for the [Jitar](https://jitar.dev) runtime. + +For more information about Jitar: + +* [Visit our website](https://jitar.dev) +* [Read the documentation](https://docs.jitar.dev). diff --git a/packages/execution/package.json b/packages/execution/package.json new file mode 100644 index 00000000..0763ca29 --- /dev/null +++ b/packages/execution/package.json @@ -0,0 +1,21 @@ +{ + "name": "@jitar/execution", + "version": "0.7.4", + "description": "Execution library for the Jitar runtime.", + "author": "Masking Technology (https://jitar.dev)", + "license": "MIT", + "type": "module", + "types": "dist/index.d.ts", + "exports": "./dist/index.js", + "private": true, + "scripts": { + "test": "vitest run", + "test-coverage": "vitest run --coverage", + "lint": "eslint . --ext .ts", + "build": "tsc -p tsconfig.json", + "clean": "rm -rf dist" + }, + "dependencies": { + "@jitar/errors": "*" + } +} diff --git a/packages/execution/src/ExecutionManager.ts b/packages/execution/src/ExecutionManager.ts new file mode 100644 index 00000000..cf6157c0 --- /dev/null +++ b/packages/execution/src/ExecutionManager.ts @@ -0,0 +1,122 @@ + +import StatusCodes from './definitions/StatusCodes'; +import RunModes from './definitions/RunModes'; + +import ImplementationNotFound from './errors/ImplementationNotFound'; +import InvalidSegment from './errors/InvalidSegment'; +import ProcedureNotFound from './errors/ProcedureNotFound'; + +import type Runner from './interfaces/Runner'; + +import type Request from './models/Request'; +import Response from './models/Response'; +import Application from './models/Application'; +import Segment from './models/Segment'; +import type Class from './models/Class'; +import type Procedure from './models/Procedure'; +import type Implementation from './models/Implementation'; +import type Version from './models/Version'; + +import ArgumentConstructor from './utils/ArgumentConstructor'; +import ErrorConverter from './utils/ErrorConverter'; + +export default class ExecutionManager implements Runner +{ + #argumentConstructor: ArgumentConstructor = new ArgumentConstructor(); + #errorConverter: ErrorConverter = new ErrorConverter(); + #application: Application = new Application(); + + async addSegment(segment: Segment): Promise + { + if ((segment instanceof Segment) === false) + { + throw new InvalidSegment(); + } + + this.#application.addSegment(segment); + } + + getClassNames(): string[] + { + return this.#application.getClassNames(); + } + + hasClass(fqn: string): boolean + { + return this.#application.hasClass(fqn); + } + + getClass(fqn: string): Class | undefined + { + return this.#application.getClass(fqn); + } + + getClassByImplementation(implementation: Function): Class | undefined + { + return this.#application.getClassByImplementation(implementation); + } + + getProcedureNames(): string[] + { + return this.#application.getProcedureNames(); + } + + hasProcedure(fqn: string): boolean + { + return this.#application.hasProcedure(fqn); + } + + getProcedure(fqn: string): Procedure | undefined + { + return this.#application.getProcedure(fqn); + } + + async run(request: Request): Promise + { + const implementation = this.#getImplementation(request.fqn, request.version); + + const args: unknown[] = this.#argumentConstructor.extract(implementation.parameters, request.args); + + if (request.mode === RunModes.DRY) + { + return new Response(StatusCodes.OK, undefined); + } + + return this.#runImplementation(request, implementation, args); + } + + #getImplementation(fqn: string, version: Version): Implementation + { + const procedure = this.#application.getProcedure(fqn); + + if (procedure === undefined) + { + throw new ProcedureNotFound(fqn); + } + + const implementation = procedure.getImplementation(version); + + if (implementation === undefined) + { + throw new ImplementationNotFound(procedure.fqn, version.toString()); + } + + return implementation; + } + + async #runImplementation(request: Request, implementation: Implementation, args: unknown[]): Promise + { + try + { + const result = await implementation.executable.call(request, ...args); + + return new Response(StatusCodes.OK, result); + } + catch (error: unknown) + { + const status = this.#errorConverter.toStatus(error); + + return new Response(status, error); + } + } +} diff --git a/packages/runtime/src/definitions/AccessLevel.ts b/packages/execution/src/definitions/AccessLevel.ts similarity index 88% rename from packages/runtime/src/definitions/AccessLevel.ts rename to packages/execution/src/definitions/AccessLevel.ts index 10130dcc..3bf6bd18 100644 --- a/packages/runtime/src/definitions/AccessLevel.ts +++ b/packages/execution/src/definitions/AccessLevel.ts @@ -6,8 +6,6 @@ const AccessLevels = PUBLIC: 'public' } as const; -Object.freeze(AccessLevels); - type Keys = keyof typeof AccessLevels; type AccessLevel = typeof AccessLevels[Keys]; diff --git a/packages/execution/src/definitions/RunModes.ts b/packages/execution/src/definitions/RunModes.ts new file mode 100644 index 00000000..ba39809d --- /dev/null +++ b/packages/execution/src/definitions/RunModes.ts @@ -0,0 +1,10 @@ + +const RunModes = +{ + NORMAL: 'normal', + DRY: 'dry', +} as const; + +export type RunMode = typeof RunModes[keyof typeof RunModes]; + +export default RunModes; diff --git a/packages/execution/src/definitions/StatusCodes.ts b/packages/execution/src/definitions/StatusCodes.ts new file mode 100644 index 00000000..49bb5fdb --- /dev/null +++ b/packages/execution/src/definitions/StatusCodes.ts @@ -0,0 +1,15 @@ + +const StatusCodes = +{ + OK: 200, + BAD_REQUEST: 400, + UNAUTHORIZED: 401, + PAYMENT_REQUIRED: 402, + FORBIDDEN: 403, + NOT_FOUND: 404, + TEAPOT: 418, + SERVER_ERROR: 500, + NOT_IMPLEMENTED: 501 +} as const; + +export default StatusCodes; diff --git a/packages/runtime/src/errors/ImplementationNotFound.ts b/packages/execution/src/errors/ImplementationNotFound.ts similarity index 70% rename from packages/runtime/src/errors/ImplementationNotFound.ts rename to packages/execution/src/errors/ImplementationNotFound.ts index d3600556..0dd3e78d 100644 --- a/packages/runtime/src/errors/ImplementationNotFound.ts +++ b/packages/execution/src/errors/ImplementationNotFound.ts @@ -1,7 +1,5 @@ -import { Loadable } from '@jitar/serialization'; - -import NotFound from './generic/NotFound.js'; +import { NotFound } from '@jitar/errors'; export default class ImplementationNotFound extends NotFound { @@ -20,5 +18,3 @@ export default class ImplementationNotFound extends NotFound get version() { return this.#version; } } - -(ImplementationNotFound as Loadable).source = 'RUNTIME_ERROR_LOCATION'; diff --git a/packages/runtime/src/errors/InvalidParameterValue.ts b/packages/execution/src/errors/InvalidParameterValue.ts similarity index 64% rename from packages/runtime/src/errors/InvalidParameterValue.ts rename to packages/execution/src/errors/InvalidParameterValue.ts index 597600bf..76f0cc1a 100644 --- a/packages/runtime/src/errors/InvalidParameterValue.ts +++ b/packages/execution/src/errors/InvalidParameterValue.ts @@ -1,7 +1,5 @@ -import { Loadable } from '@jitar/serialization'; - -import BadRequest from './generic/BadRequest.js'; +import { BadRequest } from '@jitar/errors'; export default class InvalidParameterValue extends BadRequest { @@ -16,5 +14,3 @@ export default class InvalidParameterValue extends BadRequest get parameterName() { return this.#parameterName; } } - -(InvalidParameterValue as Loadable).source = 'RUNTIME_ERROR_LOCATION'; diff --git a/packages/execution/src/errors/InvalidSegment.ts b/packages/execution/src/errors/InvalidSegment.ts new file mode 100644 index 00000000..95afd423 --- /dev/null +++ b/packages/execution/src/errors/InvalidSegment.ts @@ -0,0 +1,10 @@ + +import { ServerError } from '@jitar/errors'; + +export default class InvalidSegment extends ServerError +{ + constructor() + { + super('Invalid segment'); + } +} diff --git a/packages/runtime/src/errors/InvalidVersionNumber.ts b/packages/execution/src/errors/InvalidVersionNumber.ts similarity index 60% rename from packages/runtime/src/errors/InvalidVersionNumber.ts rename to packages/execution/src/errors/InvalidVersionNumber.ts index 8bc83f61..81761e5b 100644 --- a/packages/runtime/src/errors/InvalidVersionNumber.ts +++ b/packages/execution/src/errors/InvalidVersionNumber.ts @@ -1,7 +1,5 @@ -import { Loadable } from '@jitar/serialization'; - -import BadRequest from './generic/BadRequest.js'; +import { BadRequest } from '@jitar/errors'; export default class InvalidVersionNumber extends BadRequest { @@ -16,5 +14,3 @@ export default class InvalidVersionNumber extends BadRequest get number() { return this.#number; } } - -(InvalidVersionNumber as Loadable).source = 'RUNTIME_ERROR_LOCATION'; diff --git a/packages/runtime/src/errors/MissingParameterValue.ts b/packages/execution/src/errors/MissingParameterValue.ts similarity index 64% rename from packages/runtime/src/errors/MissingParameterValue.ts rename to packages/execution/src/errors/MissingParameterValue.ts index 57ccb3bc..9339f1f4 100644 --- a/packages/runtime/src/errors/MissingParameterValue.ts +++ b/packages/execution/src/errors/MissingParameterValue.ts @@ -1,7 +1,5 @@ -import { Loadable } from '@jitar/serialization'; - -import BadRequest from './generic/BadRequest.js'; +import { BadRequest } from '@jitar/errors'; export default class MissingParameterValue extends BadRequest { @@ -16,5 +14,3 @@ export default class MissingParameterValue extends BadRequest get parameterName() { return this.#parameterName; } } - -(MissingParameterValue as Loadable).source = 'RUNTIME_ERROR_LOCATION'; diff --git a/packages/execution/src/errors/ProcedureNotAccessible.ts b/packages/execution/src/errors/ProcedureNotAccessible.ts new file mode 100644 index 00000000..d52d7580 --- /dev/null +++ b/packages/execution/src/errors/ProcedureNotAccessible.ts @@ -0,0 +1,10 @@ + +import { Forbidden } from '@jitar/errors'; + +export default class ProcedureNotAccessible extends Forbidden +{ + constructor(fqn: string, versionNumber: string) + { + super(`Procedure '${fqn}' (v${versionNumber}) is not accessible`); + } +} diff --git a/packages/runtime/src/errors/ProcedureNotFound.ts b/packages/execution/src/errors/ProcedureNotFound.ts similarity index 58% rename from packages/runtime/src/errors/ProcedureNotFound.ts rename to packages/execution/src/errors/ProcedureNotFound.ts index d0b89fe5..54091614 100644 --- a/packages/runtime/src/errors/ProcedureNotFound.ts +++ b/packages/execution/src/errors/ProcedureNotFound.ts @@ -1,7 +1,5 @@ -import { Loadable } from '@jitar/serialization'; - -import NotFound from './generic/NotFound.js'; +import { NotFound } from '@jitar/errors'; export default class ProcedureNotFound extends NotFound { @@ -16,5 +14,3 @@ export default class ProcedureNotFound extends NotFound get fqn() { return this.#fqn; } } - -(ProcedureNotFound as Loadable).source = 'RUNTIME_ERROR_LOCATION'; diff --git a/packages/runtime/src/errors/UnknownParameter.ts b/packages/execution/src/errors/UnknownParameter.ts similarity index 64% rename from packages/runtime/src/errors/UnknownParameter.ts rename to packages/execution/src/errors/UnknownParameter.ts index 02d097df..268d028f 100644 --- a/packages/runtime/src/errors/UnknownParameter.ts +++ b/packages/execution/src/errors/UnknownParameter.ts @@ -1,7 +1,5 @@ -import { Loadable } from '@jitar/serialization'; - -import BadRequest from './generic/BadRequest.js'; +import { BadRequest } from '@jitar/errors'; export default class UnknownParameter extends BadRequest { @@ -16,5 +14,3 @@ export default class UnknownParameter extends BadRequest get parameterName() { return this.#parameterName; } } - -(UnknownParameter as Loadable).source = 'RUNTIME_ERROR_LOCATION'; diff --git a/packages/execution/src/index.ts b/packages/execution/src/index.ts new file mode 100644 index 00000000..033812aa --- /dev/null +++ b/packages/execution/src/index.ts @@ -0,0 +1,27 @@ + +export { AccessLevels, AccessLevel } from './definitions/AccessLevel'; +export { default as RunModes, RunMode } from './definitions/RunModes'; +export { default as StatusCodes } from './definitions/StatusCodes'; + +export { default as InvalidVersionNumber } from './errors/InvalidVersionNumber'; +export { default as ProcedureNotFound } from './errors/ProcedureNotFound'; +export { default as ProcedureNotAccessible } from './errors/ProcedureNotAccessible'; + +export { default as Runner } from './interfaces/Runner'; + +export { default as Segment } from './models/Segment'; +export { default as Class } from './models/Class'; +export { default as Procedure } from './models/Procedure'; +export { default as Implementation } from './models/Implementation'; +export { default as Version } from './models/Version'; +export { default as NamedParameter } from './models/NamedParameter'; +export { default as ArrayParameter } from './models/ArrayParameter'; +export { default as ObjectParameter } from './models/ObjectParameter'; +export { default as Request } from './models/Request'; +export { default as Response } from './models/Response'; + +export { default as ArgumentConstructor } from './utils/ArgumentConstructor'; +export { default as ErrorConverter } from './utils/ErrorConverter'; +export { default as VersionParser } from './utils/VersionParser'; + +export { default as ExecutionManager } from './ExecutionManager'; diff --git a/packages/execution/src/interfaces/Runner.ts b/packages/execution/src/interfaces/Runner.ts new file mode 100644 index 00000000..b1eb94e8 --- /dev/null +++ b/packages/execution/src/interfaces/Runner.ts @@ -0,0 +1,10 @@ + +import type Request from '../models/Request'; +import type Response from '../models/Response'; + +interface Runner +{ + run(request: Request): Promise; +} + +export default Runner; diff --git a/packages/execution/src/models/Application.ts b/packages/execution/src/models/Application.ts new file mode 100644 index 00000000..661e964b --- /dev/null +++ b/packages/execution/src/models/Application.ts @@ -0,0 +1,97 @@ + +import type Segment from './Segment'; +import type Class from './Class'; +import type Procedure from './Procedure'; + +export default class Application +{ + #segments: Map = new Map(); + + addSegment(segment: Segment): void + { + this.#segments.set(segment.id, segment); + } + + getClassNames(): string[] + { + const names: Set = new Set(); + + for (const segment of this.#segments.values()) + { + const classes = segment.getClasses(); + + classes.forEach(clazz => names.add(clazz.fqn)); + } + + return [...names.values()]; + } + + hasClass(fqn: string): boolean + { + const classNames = this.getClassNames(); + + return classNames.includes(fqn); + } + + getClass(fqn: string): Class | undefined + { + for (const segment of this.#segments.values()) + { + if (segment.hasClass(fqn)) + { + return segment.getClass(fqn); + } + } + + return undefined; + } + + getClassByImplementation(implementation: Function): Class | undefined + { + for (const segment of this.#segments.values()) + { + const clazz = segment.getClassByImplementation(implementation); + + if (clazz !== undefined) + { + return clazz; + } + } + + return undefined; + } + + getProcedureNames(): string[] + { + const names: Set = new Set(); + + for (const segment of this.#segments.values()) + { + const procedures = segment.getExposedProcedures(); + + procedures.forEach(procedure => names.add(procedure.fqn)); + } + + return [...names.values()]; + } + + hasProcedure(fqn: string): boolean + { + const procedureNames = this.getProcedureNames(); + + return procedureNames.includes(fqn); + } + + getProcedure(fqn: string): Procedure | undefined + { + for (const segment of this.#segments.values()) + { + if (segment.hasProcedure(fqn)) + { + return segment.getProcedure(fqn); + } + } + + return undefined; + } +} diff --git a/packages/runtime/src/models/ArrayParameter.ts b/packages/execution/src/models/ArrayParameter.ts similarity index 60% rename from packages/runtime/src/models/ArrayParameter.ts rename to packages/execution/src/models/ArrayParameter.ts index bfe36170..c852f7bc 100644 --- a/packages/runtime/src/models/ArrayParameter.ts +++ b/packages/execution/src/models/ArrayParameter.ts @@ -1,5 +1,5 @@ -import DestructuredParameter from './DestructuredParameter.js'; +import DestructuredParameter from './DestructuredParameter'; export default class ArrayParameter extends DestructuredParameter { diff --git a/packages/execution/src/models/Class.ts b/packages/execution/src/models/Class.ts new file mode 100644 index 00000000..bfc4a507 --- /dev/null +++ b/packages/execution/src/models/Class.ts @@ -0,0 +1,16 @@ + +export default class Class +{ + #fqn: string; + #implementation: Function; + + constructor(fqn: string, implementation: Function) + { + this.#fqn = fqn; + this.#implementation = implementation; + } + + get fqn() { return this.#fqn; } + + get implementation() { return this.#implementation; } +} diff --git a/packages/runtime/src/models/DestructuredParameter.ts b/packages/execution/src/models/DestructuredParameter.ts similarity index 88% rename from packages/runtime/src/models/DestructuredParameter.ts rename to packages/execution/src/models/DestructuredParameter.ts index cd51ae79..9d710568 100644 --- a/packages/runtime/src/models/DestructuredParameter.ts +++ b/packages/execution/src/models/DestructuredParameter.ts @@ -1,5 +1,5 @@ -import Parameter from './Parameter.js'; +import Parameter from './Parameter'; export default class DestructuredParameter extends Parameter { diff --git a/packages/runtime/src/models/Implementation.ts b/packages/execution/src/models/Implementation.ts similarity index 82% rename from packages/runtime/src/models/Implementation.ts rename to packages/execution/src/models/Implementation.ts index f34af909..7981227e 100644 --- a/packages/runtime/src/models/Implementation.ts +++ b/packages/execution/src/models/Implementation.ts @@ -1,7 +1,8 @@ -import { AccessLevels, AccessLevel } from '../definitions/AccessLevel.js'; -import Parameter from './Parameter.js'; -import Version from './Version.js'; +import { AccessLevels, AccessLevel } from '../definitions/AccessLevel'; + +import type Parameter from './Parameter'; +import type Version from './Version'; export default class Implementation { @@ -24,6 +25,8 @@ export default class Implementation get protected() { return this.#access === AccessLevels.PROTECTED; } + get private() { return this.#access === AccessLevels.PRIVATE; } + get parameters() { return this.#parameters; } get executable() { return this.#executable; } diff --git a/packages/runtime/src/models/NamedParameter.ts b/packages/execution/src/models/NamedParameter.ts similarity index 68% rename from packages/runtime/src/models/NamedParameter.ts rename to packages/execution/src/models/NamedParameter.ts index c48e8d48..4862d330 100644 --- a/packages/runtime/src/models/NamedParameter.ts +++ b/packages/execution/src/models/NamedParameter.ts @@ -1,5 +1,5 @@ -import Parameter from './Parameter.js'; +import Parameter from './Parameter'; export default class NamedParameter extends Parameter { diff --git a/packages/runtime/src/models/ObjectParameter.ts b/packages/execution/src/models/ObjectParameter.ts similarity index 61% rename from packages/runtime/src/models/ObjectParameter.ts rename to packages/execution/src/models/ObjectParameter.ts index f1dc67c6..98353033 100644 --- a/packages/runtime/src/models/ObjectParameter.ts +++ b/packages/execution/src/models/ObjectParameter.ts @@ -1,5 +1,5 @@ -import DestructuredParameter from './DestructuredParameter.js'; +import DestructuredParameter from './DestructuredParameter'; export default class ObjectParameter extends DestructuredParameter { diff --git a/packages/runtime/src/models/Parameter.ts b/packages/execution/src/models/Parameter.ts similarity index 100% rename from packages/runtime/src/models/Parameter.ts rename to packages/execution/src/models/Parameter.ts diff --git a/packages/runtime/src/models/Procedure.ts b/packages/execution/src/models/Procedure.ts similarity index 76% rename from packages/runtime/src/models/Procedure.ts rename to packages/execution/src/models/Procedure.ts index 5de91d9c..0dc9fce1 100644 --- a/packages/runtime/src/models/Procedure.ts +++ b/packages/execution/src/models/Procedure.ts @@ -1,6 +1,6 @@ -import Implementation from './Implementation.js'; -import Version from './Version.js'; +import type Implementation from './Implementation'; +import Version from './Version'; export default class Procedure { @@ -15,27 +15,15 @@ export default class Procedure get fqn() { return this.#fqn; } - get public() + get public(): boolean { - // If at least one implementation is public, the procedure is public - // Public procedures can be called from outside the segment - const implementations = [...this.#implementations.values()]; return implementations.some(implementation => implementation.public); } - get protected() + get protected(): boolean { - // Public procedures take precedence over protected procedures in case of duplicates - // If at least one implementation is protected, the procedure is protected - // Protected procedures can be called from outside the segment with a key - - if (this.public) - { - return false; - } - const implementations = [...this.#implementations.values()]; return implementations.some(implementation => implementation.protected); diff --git a/packages/runtime/src/models/Request.ts b/packages/execution/src/models/Request.ts similarity index 86% rename from packages/runtime/src/models/Request.ts rename to packages/execution/src/models/Request.ts index 096ad3e7..4ef0ab2c 100644 --- a/packages/runtime/src/models/Request.ts +++ b/packages/execution/src/models/Request.ts @@ -1,5 +1,7 @@ -import Version from './Version.js'; +import type { RunMode } from '../definitions/RunModes'; + +import type Version from './Version'; export default class Request { @@ -7,13 +9,15 @@ export default class Request #version: Version; #args: Map; #headers: Map = new Map(); + #mode: RunMode; - constructor(fqn: string, version: Version, args: Map, headers: Map) + constructor(fqn: string, version: Version, args: Map, headers: Map, mode: RunMode) { this.#fqn = fqn; this.#version = version; this.#args = args; this.#headers = headers; + this.#mode = mode; } get fqn() { return this.#fqn; } @@ -24,6 +28,8 @@ export default class Request get headers() { return this.#headers; } + get mode() { return this.#mode; } + clearArguments(): void { this.#args.clear(); diff --git a/packages/runtime/src/models/Response.ts b/packages/execution/src/models/Response.ts similarity index 82% rename from packages/runtime/src/models/Response.ts rename to packages/execution/src/models/Response.ts index bc33008b..905c85d3 100644 --- a/packages/runtime/src/models/Response.ts +++ b/packages/execution/src/models/Response.ts @@ -1,15 +1,19 @@ export default class Response { + #status: number; #result: unknown; #headers: Map; - constructor(result: unknown = undefined, headers = new Map()) + constructor(status: number, result: unknown = undefined, headers = new Map()) { + this.#status = status; this.#result = result; this.#headers = headers; } + get status() { return this.#status; } + get result() { return this.#result; } set result(value: unknown) { this.#result = value; } diff --git a/packages/execution/src/models/Segment.ts b/packages/execution/src/models/Segment.ts new file mode 100644 index 00000000..d6619ded --- /dev/null +++ b/packages/execution/src/models/Segment.ts @@ -0,0 +1,72 @@ + +import type Class from './Class'; +import type Procedure from './Procedure'; + +export default class Segment +{ + #id: string; + #classes: Map = new Map(); + #procedures: Map = new Map(); + + constructor(id: string) + { + this.#id = id; + } + + get id() { return this.#id; } + + addClass(clazz: Class): Segment + { + this.#classes.set(clazz.fqn, clazz); + + return this; + } + + hasClass(fqn: string): boolean + { + const clazz = this.getClass(fqn); + + return clazz !== undefined; + } + + getClass(fqn: string): Class | undefined + { + return this.#classes.get(fqn); + } + + getClassByImplementation(implementation: Function): Class | undefined + { + const classes = this.getClasses(); + + return classes.find(clazz => clazz.implementation === implementation); + } + + getClasses(): Class[] + { + return [...this.#classes.values()]; + } + + addProcedure(procedure: Procedure): Segment + { + this.#procedures.set(procedure.fqn, procedure); + + return this; + } + + hasProcedure(fqn: string): boolean + { + return this.#procedures.has(fqn); + } + + getProcedure(fqn: string): Procedure | undefined + { + return this.#procedures.get(fqn); + } + + getExposedProcedures(): Procedure[] + { + const procedures = [...this.#procedures.values()]; + + return procedures.filter(procedure => procedure.public || procedure.protected); + } +} diff --git a/packages/runtime/src/models/Version.ts b/packages/execution/src/models/Version.ts similarity index 100% rename from packages/runtime/src/models/Version.ts rename to packages/execution/src/models/Version.ts diff --git a/packages/runtime/src/utils/ArgumentConstructor.ts b/packages/execution/src/utils/ArgumentConstructor.ts similarity index 93% rename from packages/runtime/src/utils/ArgumentConstructor.ts rename to packages/execution/src/utils/ArgumentConstructor.ts index 97cf723e..1557ce8f 100644 --- a/packages/runtime/src/utils/ArgumentConstructor.ts +++ b/packages/execution/src/utils/ArgumentConstructor.ts @@ -1,13 +1,13 @@ -import UnknownParameter from '../errors/UnknownParameter.js'; -import MissingParameterValue from '../errors/MissingParameterValue.js'; -import InvalidParameterValue from '../errors/InvalidParameterValue.js'; +import UnknownParameter from '../errors/UnknownParameter'; +import MissingParameterValue from '../errors/MissingParameterValue'; +import InvalidParameterValue from '../errors/InvalidParameterValue'; -import ArrayParameter from '../models/ArrayParameter.js'; -import DestructuredParameter from '../models/DestructuredParameter.js'; -import NamedParameter from '../models/NamedParameter.js'; -import ObjectParameter from '../models/ObjectParameter.js'; -import Parameter from '../models/Parameter.js'; +import ArrayParameter from '../models/ArrayParameter'; +import DestructuredParameter from '../models/DestructuredParameter'; +import NamedParameter from '../models/NamedParameter'; +import ObjectParameter from '../models/ObjectParameter'; +import type Parameter from '../models/Parameter'; const OPTIONAL_ARGUMENT_PREFIX = '*'; const OPTIONAL_ARGUMENT_PREFIX_LENGTH = OPTIONAL_ARGUMENT_PREFIX.length; @@ -28,7 +28,7 @@ export default class ArgumentExtractor if (argsCopy.size > 0) { - const name = argsCopy.keys().next().value; + const name = argsCopy.keys().next().value as string; throw new UnknownParameter(name); } diff --git a/packages/execution/src/utils/ErrorConverter.ts b/packages/execution/src/utils/ErrorConverter.ts new file mode 100644 index 00000000..bbd3f9ab --- /dev/null +++ b/packages/execution/src/utils/ErrorConverter.ts @@ -0,0 +1,35 @@ + +import { BadRequest, Forbidden, NotFound, NotImplemented, PaymentRequired, ServerError, Teapot, Unauthorized } from '@jitar/errors'; + +import StatusCodes from '../definitions/StatusCodes'; + +export default class ErrorConverter +{ + toStatus(error: unknown): number + { + if (error instanceof BadRequest) return StatusCodes.BAD_REQUEST; + if (error instanceof Forbidden) return StatusCodes.FORBIDDEN; + if (error instanceof NotFound) return StatusCodes.NOT_FOUND; + if (error instanceof NotImplemented) return StatusCodes.NOT_IMPLEMENTED; + if (error instanceof PaymentRequired) return StatusCodes.PAYMENT_REQUIRED; + if (error instanceof Teapot) return StatusCodes.TEAPOT; + if (error instanceof Unauthorized) return StatusCodes.UNAUTHORIZED; + + return StatusCodes.SERVER_ERROR; + } + + fromStatus(status: number, message?: string): unknown + { + switch (status) + { + case StatusCodes.BAD_REQUEST: return new BadRequest(message); + case StatusCodes.FORBIDDEN: return new Forbidden(message); + case StatusCodes.NOT_FOUND: return new NotFound(message); + case StatusCodes.NOT_IMPLEMENTED: return new NotImplemented(message); + case StatusCodes.PAYMENT_REQUIRED: return new PaymentRequired(message); + case StatusCodes.TEAPOT: return new Teapot(message); + case StatusCodes.UNAUTHORIZED: return new Unauthorized(message); + default: return new ServerError(message); + } + } +} diff --git a/packages/runtime/src/utils/VersionParser.ts b/packages/execution/src/utils/VersionParser.ts similarity index 89% rename from packages/runtime/src/utils/VersionParser.ts rename to packages/execution/src/utils/VersionParser.ts index 90aa0baf..323b0b2a 100644 --- a/packages/runtime/src/utils/VersionParser.ts +++ b/packages/execution/src/utils/VersionParser.ts @@ -1,13 +1,13 @@ -import InvalidVersionNumber from '../errors/InvalidVersionNumber.js'; +import InvalidVersionNumber from '../errors/InvalidVersionNumber'; -import Version from '../models/Version.js'; +import Version from '../models/Version'; const VERSION_EXPRESSION = /^\d+(?:\.\d+){0,2}$/; export default class VersionParser { - static parse(number: string): Version + parse(number: string): Version { if (number.trim().length === 0) { diff --git a/packages/execution/test/ExecutionManager.spec.ts b/packages/execution/test/ExecutionManager.spec.ts new file mode 100644 index 00000000..bb1781a9 --- /dev/null +++ b/packages/execution/test/ExecutionManager.spec.ts @@ -0,0 +1,68 @@ + +import { describe, expect, it } from 'vitest'; + +import RunModes from '../src/definitions/RunModes'; +import InvalidSegment from '../src/errors/InvalidSegment'; +import ImplementationNotFound from '../src/errors/ImplementationNotFound'; +import ProcedureNotFound from '../src/errors/ProcedureNotFound'; +import Request from '../src/models/Request'; +import Version from '../src/models/Version'; +import type Segment from '../src/models/Segment'; + +import { EXECUTION_MANAGERS } from './fixtures'; + +const executionManager = EXECUTION_MANAGERS.GENERAL; + +describe('ExecutionManager', () => +{ + describe('.addSegment(segment)', () => + { + // Adding a valid segment is already done in the fixture + + it('should not add an invalid segment', async () => + { + const promise = executionManager.addSegment({} as Segment); + + expect(promise).rejects.toEqual(new InvalidSegment()); + }); + }); + + describe('.run(request)', () => + { + it('should run an existing procedure implementation', async () => + { + const request = new Request('public', Version.DEFAULT, new Map(), new Map(), RunModes.NORMAL); + + const response = await executionManager.run(request); + + expect(response.result).toBe('public'); + }); + + it('should not run a non-existing procedure', () => + { + const request = new Request('nonExisting', Version.DEFAULT, new Map(), new Map(), RunModes.NORMAL); + + const promise = executionManager.run(request); + + expect(promise).rejects.toEqual(new ProcedureNotFound('nonExisting')); + }); + + it('should not run a non-existing implementation', async () => + { + const request = new Request('versioned', Version.DEFAULT, new Map(), new Map(), RunModes.NORMAL); + + const promise = executionManager.run(request); + + expect(promise).rejects.toEqual(new ImplementationNotFound('versioned', Version.DEFAULT.toString())); + }); + + it('should perform a dry-run', async () => + { + const request = new Request('public', Version.DEFAULT, new Map(), new Map(), RunModes.DRY); + + const response = await executionManager.run(request); + + expect(response.result).toBeUndefined(); + }); + }); +}); diff --git a/packages/execution/test/fixtures/executionManagers.fixture.ts b/packages/execution/test/fixtures/executionManagers.fixture.ts new file mode 100644 index 00000000..3db02e18 --- /dev/null +++ b/packages/execution/test/fixtures/executionManagers.fixture.ts @@ -0,0 +1,12 @@ + +import ExecutionManager from '../../src/ExecutionManager'; + +import { SEGMENTS } from '../models/fixtures'; + +const generalManager = new ExecutionManager(); +generalManager.addSegment(SEGMENTS.GENERAL); + +export const EXECUTION_MANAGERS = +{ + GENERAL: generalManager +}; diff --git a/packages/execution/test/fixtures/index.ts b/packages/execution/test/fixtures/index.ts new file mode 100644 index 00000000..29f72f0a --- /dev/null +++ b/packages/execution/test/fixtures/index.ts @@ -0,0 +1,2 @@ + +export * from './executionManagers.fixture'; diff --git a/packages/execution/test/models/Application.spec.ts b/packages/execution/test/models/Application.spec.ts new file mode 100644 index 00000000..0bd30918 --- /dev/null +++ b/packages/execution/test/models/Application.spec.ts @@ -0,0 +1,35 @@ + +import { describe, expect, it } from 'vitest'; + +import { APPLICATIONS } from './fixtures'; + +const application = APPLICATIONS.GENERAL; + +describe('models/Application', () => +{ + // TODO: Add classes tests + + describe('.getProcedureNames()', () => + { + it('should contain all public and protected procedure names', () => + { + const procedureNames = application.getProcedureNames(); + + expect(procedureNames).toHaveLength(2); + expect(procedureNames).toContain('protected'); + expect(procedureNames).toContain('public'); + }); + }); + + describe('.hasProcedure(name)', () => + { + it('should have public procedures', () => + { + const hasProtectedProcedure = application.hasProcedure('protected'); + const hasPublicProcedure = application.hasProcedure('public'); + + expect(hasProtectedProcedure).toBeTruthy(); + expect(hasPublicProcedure).toBeTruthy(); + }); + }); +}); diff --git a/packages/runtime/test/models/Implementation.spec.ts b/packages/execution/test/models/Implementation.spec.ts similarity index 94% rename from packages/runtime/test/models/Implementation.spec.ts rename to packages/execution/test/models/Implementation.spec.ts index d34d2157..5edd80ec 100644 --- a/packages/runtime/test/models/Implementation.spec.ts +++ b/packages/execution/test/models/Implementation.spec.ts @@ -1,7 +1,7 @@ import { describe, expect, it } from 'vitest'; -import { IMPLEMENTATIONS } from '../_fixtures/models/Implementation.fixture'; +import { IMPLEMENTATIONS } from './fixtures'; const privateImplementation = IMPLEMENTATIONS.PRIVATE; const publicImplementation = IMPLEMENTATIONS.PUBLIC; diff --git a/packages/runtime/test/models/Procedure.spec.ts b/packages/execution/test/models/Procedure.spec.ts similarity index 96% rename from packages/runtime/test/models/Procedure.spec.ts rename to packages/execution/test/models/Procedure.spec.ts index 2056ff50..1e070823 100644 --- a/packages/runtime/test/models/Procedure.spec.ts +++ b/packages/execution/test/models/Procedure.spec.ts @@ -3,7 +3,7 @@ import { describe, expect, it } from 'vitest'; import Version from '../../src/models/Version'; -import { PROCEDURES } from '../_fixtures/models/Procedure.fixture'; +import { PROCEDURES } from './fixtures'; const privateProcedure = PROCEDURES.PRIVATE; const publicProcedure = PROCEDURES.PUBLIC; diff --git a/packages/runtime/test/models/Segment.spec.ts b/packages/execution/test/models/Segment.spec.ts similarity index 94% rename from packages/runtime/test/models/Segment.spec.ts rename to packages/execution/test/models/Segment.spec.ts index a39ca52a..5c073189 100644 --- a/packages/runtime/test/models/Segment.spec.ts +++ b/packages/execution/test/models/Segment.spec.ts @@ -1,7 +1,7 @@ import { describe, expect, it } from 'vitest'; -import { SEGMENTS } from '../_fixtures/models/Segment.fixture'; +import { SEGMENTS } from './fixtures'; const generalSegment = SEGMENTS.GENERAL; diff --git a/packages/runtime/test/models/Version.spec.ts b/packages/execution/test/models/Version.spec.ts similarity index 96% rename from packages/runtime/test/models/Version.spec.ts rename to packages/execution/test/models/Version.spec.ts index 8357d380..b89cabd4 100644 --- a/packages/runtime/test/models/Version.spec.ts +++ b/packages/execution/test/models/Version.spec.ts @@ -1,7 +1,7 @@ import { describe, expect, it } from 'vitest'; -import { VERSIONS } from '../_fixtures/models/Version.fixture'; +import { VERSIONS } from './fixtures'; const actualVersion = VERSIONS.ACTUAL; diff --git a/packages/execution/test/models/fixtures/applications.fixture.ts b/packages/execution/test/models/fixtures/applications.fixture.ts new file mode 100644 index 00000000..b994ec37 --- /dev/null +++ b/packages/execution/test/models/fixtures/applications.fixture.ts @@ -0,0 +1,13 @@ + +import Application from '../../../src/models/Application'; + +import { SEGMENTS } from './segments.fixture'; + +const generalApplication = new Application(); +generalApplication.addSegment(SEGMENTS.GENERAL); +// TODO: Add more segments + +export const APPLICATIONS = +{ + GENERAL: generalApplication +}; diff --git a/packages/execution/test/models/fixtures/executables.fixture.ts b/packages/execution/test/models/fixtures/executables.fixture.ts new file mode 100644 index 00000000..6c07e18c --- /dev/null +++ b/packages/execution/test/models/fixtures/executables.fixture.ts @@ -0,0 +1,13 @@ + +export const EXECUTABLES = +{ + PRIVATE: () => { return 'private'; }, + PROTECTED: () => { return 'protected'; }, + PUBLIC: () => { return 'public'; }, + PARAMETERS: (mandatory: string, optional = 'default') => { return `${mandatory} ${optional}`; }, + BROKEN: () => { throw new Error('broken'); }, + CONTEXT: () => { return this; }, + V1_0_0: () => { return '1.0.0'; }, + V1_0_5: () => { return '1.0.5'; }, + V1_1_0: () => { return '1.1.0'; } +}; diff --git a/packages/runtime/test/_fixtures/models/Implementation.fixture.ts b/packages/execution/test/models/fixtures/implementations.fixture.ts similarity index 56% rename from packages/runtime/test/_fixtures/models/Implementation.fixture.ts rename to packages/execution/test/models/fixtures/implementations.fixture.ts index d7cbbb40..2ae01bf3 100644 --- a/packages/runtime/test/_fixtures/models/Implementation.fixture.ts +++ b/packages/execution/test/models/fixtures/implementations.fixture.ts @@ -1,14 +1,14 @@ import { AccessLevels } from '../../../src/definitions/AccessLevel'; + import Implementation from '../../../src/models/Implementation'; import Version from '../../../src/models/Version'; -import { EXECUTABLES } from './Executable.fixture'; -import { PARAMETERS } from './Parameter.fixture'; +import { EXECUTABLES } from './executables.fixture'; +import { PARAMETERS } from './parameters.fixture'; -const IMPLEMENTATIONS = +export const IMPLEMENTATIONS = { - // General PRIVATE: new Implementation(Version.DEFAULT, AccessLevels.PRIVATE, [], EXECUTABLES.PRIVATE), PROTECTED: new Implementation(Version.DEFAULT, AccessLevels.PROTECTED, [], EXECUTABLES.PROTECTED), PUBLIC: new Implementation(Version.DEFAULT, AccessLevels.PUBLIC, [], EXECUTABLES.PUBLIC), @@ -17,19 +17,5 @@ const IMPLEMENTATIONS = CONTEXT: new Implementation(Version.DEFAULT, AccessLevels.PRIVATE, [], EXECUTABLES.CONTEXT), V1_0_0: new Implementation(new Version(1, 0, 0), AccessLevels.PRIVATE, [], EXECUTABLES.V1_0_0), V1_0_5: new Implementation(new Version(1, 0, 5), AccessLevels.PRIVATE, [], EXECUTABLES.V1_0_5), - V1_1_0: new Implementation(new Version(1, 1, 0), AccessLevels.PRIVATE, [], EXECUTABLES.V1_1_0), - - // First segment - FIRST: new Implementation(Version.DEFAULT, AccessLevels.PRIVATE, [], EXECUTABLES.FIRST), - SECOND: new Implementation(Version.DEFAULT, AccessLevels.PUBLIC, [], EXECUTABLES.SECOND), - THIRD: new Implementation(Version.DEFAULT, AccessLevels.PUBLIC, [], EXECUTABLES.THIRD), - - // Second segment - FOURTH: new Implementation(Version.DEFAULT, AccessLevels.PUBLIC, [], EXECUTABLES.FOURTH), - FIFTH: new Implementation(Version.DEFAULT, AccessLevels.PRIVATE, [], EXECUTABLES.FIFTH), - SIXTH: new Implementation(Version.DEFAULT, AccessLevels.PUBLIC, [], EXECUTABLES.SIXTH) + V1_1_0: new Implementation(new Version(1, 1, 0), AccessLevels.PRIVATE, [], EXECUTABLES.V1_1_0) }; - -Object.freeze(IMPLEMENTATIONS); - -export { IMPLEMENTATIONS }; diff --git a/packages/execution/test/models/fixtures/index.ts b/packages/execution/test/models/fixtures/index.ts new file mode 100644 index 00000000..a007d30e --- /dev/null +++ b/packages/execution/test/models/fixtures/index.ts @@ -0,0 +1,8 @@ + +export * from './applications.fixture'; +export * from './executables.fixture'; +export * from './implementations.fixture'; +export * from './parameters.fixture'; +export * from './procedures.fixture'; +export * from './segments.fixture'; +export * from './versions.fixture'; diff --git a/packages/runtime/test/_fixtures/models/Parameter.fixture.ts b/packages/execution/test/models/fixtures/parameters.fixture.ts similarity index 71% rename from packages/runtime/test/_fixtures/models/Parameter.fixture.ts rename to packages/execution/test/models/fixtures/parameters.fixture.ts index e7490e16..b3dabd6e 100644 --- a/packages/runtime/test/_fixtures/models/Parameter.fixture.ts +++ b/packages/execution/test/models/fixtures/parameters.fixture.ts @@ -1,12 +1,8 @@ import NamedParameter from '../../../src/models/NamedParameter'; -const PARAMETERS = +export const PARAMETERS = { MANDATORY: new NamedParameter('mandatory', false), OPTIONAL: new NamedParameter('optional', true) }; - -Object.freeze(PARAMETERS); - -export { PARAMETERS }; diff --git a/packages/runtime/test/_fixtures/models/Procedure.fixture.ts b/packages/execution/test/models/fixtures/procedures.fixture.ts similarity index 50% rename from packages/runtime/test/_fixtures/models/Procedure.fixture.ts rename to packages/execution/test/models/fixtures/procedures.fixture.ts index 3b3064a4..060e92ce 100644 --- a/packages/runtime/test/_fixtures/models/Procedure.fixture.ts +++ b/packages/execution/test/models/fixtures/procedures.fixture.ts @@ -1,45 +1,30 @@ import Procedure from '../../../src/models/Procedure'; -import { IMPLEMENTATIONS } from './Implementation.fixture'; +import { IMPLEMENTATIONS } from './implementations.fixture'; -const PROCEDURES = +export const PROCEDURES = { - // General PRIVATE: new Procedure('private') .addImplementation(IMPLEMENTATIONS.PRIVATE), + PROTECTED: new Procedure('protected') .addImplementation(IMPLEMENTATIONS.PROTECTED), + PUBLIC: new Procedure('public') .addImplementation(IMPLEMENTATIONS.PUBLIC), + PARAMETERS: new Procedure('parameter') .addImplementation(IMPLEMENTATIONS.PARAMETERS), + BROKEN: new Procedure('broken') .addImplementation(IMPLEMENTATIONS.BROKEN), + CONTEXT: new Procedure('context') .addImplementation(IMPLEMENTATIONS.CONTEXT), + VERSIONED: new Procedure('versioned') .addImplementation(IMPLEMENTATIONS.V1_0_0) .addImplementation(IMPLEMENTATIONS.V1_0_5) - .addImplementation(IMPLEMENTATIONS.V1_1_0), - - // First segment - FIRST: new Procedure('first') - .addImplementation(IMPLEMENTATIONS.FIRST), - SECOND: new Procedure('second') - .addImplementation(IMPLEMENTATIONS.SECOND), - THIRD: new Procedure('third') - .addImplementation(IMPLEMENTATIONS.THIRD), - - // Second segment - FOURTH: new Procedure('fourth') - .addImplementation(IMPLEMENTATIONS.FOURTH), - FIFTH: new Procedure('fifth') - .addImplementation(IMPLEMENTATIONS.FIFTH), - SIXTH: new Procedure('sixth') - .addImplementation(IMPLEMENTATIONS.SIXTH) + .addImplementation(IMPLEMENTATIONS.V1_1_0) }; - -Object.freeze(PROCEDURES); - -export { PROCEDURES }; diff --git a/packages/execution/test/models/fixtures/segments.fixture.ts b/packages/execution/test/models/fixtures/segments.fixture.ts new file mode 100644 index 00000000..a2368b18 --- /dev/null +++ b/packages/execution/test/models/fixtures/segments.fixture.ts @@ -0,0 +1,16 @@ + +import Segment from '../../../src/models/Segment'; + +import { PROCEDURES } from './procedures.fixture'; + +export const SEGMENTS = +{ + GENERAL: new Segment('general') + .addProcedure(PROCEDURES.PRIVATE) + .addProcedure(PROCEDURES.PROTECTED) + .addProcedure(PROCEDURES.PUBLIC) + .addProcedure(PROCEDURES.PARAMETERS) + .addProcedure(PROCEDURES.BROKEN) + .addProcedure(PROCEDURES.CONTEXT) + .addProcedure(PROCEDURES.VERSIONED) +}; diff --git a/packages/runtime/test/_fixtures/models/Version.fixture.ts b/packages/execution/test/models/fixtures/versions.fixture.ts similarity index 81% rename from packages/runtime/test/_fixtures/models/Version.fixture.ts rename to packages/execution/test/models/fixtures/versions.fixture.ts index 893dd1e9..ef7b56de 100644 --- a/packages/runtime/test/_fixtures/models/Version.fixture.ts +++ b/packages/execution/test/models/fixtures/versions.fixture.ts @@ -1,7 +1,7 @@ import Version from '../../../src/models/Version'; -const VERSIONS = +export const VERSIONS = { DEFAULT: Version.DEFAULT, ACTUAL: new Version(1, 2, 3), @@ -11,7 +11,3 @@ const VERSIONS = MAJOR: new Version(1, 0, 0), MAJOR_MINOR: new Version(1, 2, 0) }; - -Object.freeze(VERSIONS); - -export { VERSIONS }; diff --git a/packages/runtime/test/utils/ArgumentConstructor.spec.ts b/packages/execution/test/utils/ArgumentConstructor.spec.ts similarity index 99% rename from packages/runtime/test/utils/ArgumentConstructor.spec.ts rename to packages/execution/test/utils/ArgumentConstructor.spec.ts index 05ef9b44..0c78a5a1 100644 --- a/packages/runtime/test/utils/ArgumentConstructor.spec.ts +++ b/packages/execution/test/utils/ArgumentConstructor.spec.ts @@ -7,7 +7,7 @@ import UnknownParameter from '../../src/errors/UnknownParameter'; import ArgumentConstructor from '../../src/utils/ArgumentConstructor'; -import { PARAMETERS, ARGUMENTS } from '../_fixtures/utils/ArgumentConstructor.fixture'; +import { PARAMETERS, ARGUMENTS } from './fixtures'; const argumentConstructor = new ArgumentConstructor(); diff --git a/packages/runtime/test/utils/VersionParser.spec.ts b/packages/execution/test/utils/VersionParser.spec.ts similarity index 70% rename from packages/runtime/test/utils/VersionParser.spec.ts rename to packages/execution/test/utils/VersionParser.spec.ts index bd8d5895..f4504d80 100644 --- a/packages/runtime/test/utils/VersionParser.spec.ts +++ b/packages/execution/test/utils/VersionParser.spec.ts @@ -4,50 +4,52 @@ import { describe, expect, it } from 'vitest'; import InvalidVersionNumber from '../../src/errors/InvalidVersionNumber'; import VersionParser from '../../src/utils/VersionParser'; -import { VERSIONS } from '../_fixtures/models/Version.fixture'; +import { VERSIONS } from './fixtures'; -describe('utils/VersionParser', () => +const versionParser = new VersionParser(); + +describe('utils/versionParser', () => { describe('.parse(number)', () => { it('should parse a default version for an empty string', () => { - const version = VersionParser.parse(''); + const version = versionParser.parse(''); expect(version).toEqual(VERSIONS.DEFAULT); }); it('should parse a major number', () => { - const version = VersionParser.parse('1'); + const version = versionParser.parse('1'); expect(version).toEqual(VERSIONS.MAJOR); }); it('should parse a major.minor number', () => { - const version = VersionParser.parse('1.2'); + const version = versionParser.parse('1.2'); expect(version).toEqual(VERSIONS.MAJOR_MINOR); }); it('should parse a major.minor.patch number', () => { - const version = VersionParser.parse('1.2.3'); + const version = versionParser.parse('1.2.3'); expect(version).toEqual(VERSIONS.ACTUAL); }); it('should not parse an invalid number', () => { - const run = () => VersionParser.parse('1.2.3.4'); + const run = () => versionParser.parse('1.2.3.4'); expect(run).toThrow(new InvalidVersionNumber('1.2.3.4')); }); it('should not parse an invalid number', () => { - const run = () => VersionParser.parse('1.2.a'); + const run = () => versionParser.parse('1.2.a'); expect(run).toThrow(new InvalidVersionNumber('1.2.a')); }); diff --git a/packages/runtime/test/_fixtures/utils/ArgumentConstructor.fixture.ts b/packages/execution/test/utils/fixtures/arguments.fixture.ts similarity index 58% rename from packages/runtime/test/_fixtures/utils/ArgumentConstructor.fixture.ts rename to packages/execution/test/utils/fixtures/arguments.fixture.ts index 01565a69..c4cc4dcd 100644 --- a/packages/runtime/test/_fixtures/utils/ArgumentConstructor.fixture.ts +++ b/packages/execution/test/utils/fixtures/arguments.fixture.ts @@ -1,38 +1,5 @@ -import ArrayParameter from '../../../src/models/ArrayParameter'; -import NamedParameter from '../../../src/models/NamedParameter'; -import ObjectParameter from '../../../src/models/ObjectParameter'; - -const PARAMETERS = -{ - NAMED: [new NamedParameter('id', false), new NamedParameter('name', false), new NamedParameter('age', true)], - ARRAY: [new ArrayParameter([new NamedParameter('query', false), new NamedParameter('sort', true)])], - OBJECT: [new ObjectParameter([new NamedParameter('query', false), new NamedParameter('sort', true)])], - MIXED: [ - new NamedParameter('id', false), - new ArrayParameter([new NamedParameter('name', false), new NamedParameter('age', true)]), - new ObjectParameter([new NamedParameter('query', false), new NamedParameter('sort', true)]) - ], - NESTED_ARRAY: [ - new ArrayParameter([ - new NamedParameter('id', false), - new ArrayParameter([new NamedParameter('name', false), new NamedParameter('age', true)]), - new ObjectParameter([new NamedParameter('query', false), new NamedParameter('sort', false)], undefined, true) - ]) - ], - NESTED_OBJECT: [ - new ObjectParameter([ - new NamedParameter('id', false), - new ArrayParameter([new NamedParameter('name', false), new NamedParameter('age', true)], 'person'), - new ObjectParameter([new NamedParameter('query', false), new NamedParameter('sort', false)], 'filter', true) - ]) - ], - REST: [new NamedParameter('...rest', false)], - REST_ARRAY: [new ArrayParameter([new NamedParameter('name', false), new NamedParameter('...rest', true)])], - REST_OBJECT: [new ObjectParameter([new NamedParameter('name', false), new NamedParameter('...rest', true)])] -}; - -const ARGUMENTS = +export const ARGUMENTS = { NAMED_ALL: new Map(Object.entries({ 'id': 1, 'name': 'John Doe', 'age': 42 })), NAMED_OPTIONAL: new Map(Object.entries({ 'id': 1, 'name': 'John Doe' })), // Misses the age @@ -66,5 +33,3 @@ const ARGUMENTS = OPTIONAL_ARGUMENTS: new Map(Object.entries({ '*id': 1, '*name': 'John Doe', '*age': 42 })), // All arguments are optional OPTIONAL_ARGUMENTS_EXTRA: new Map(Object.entries({ 'id': 1, 'name': 'John Doe', '*additional': 'argument', '*ignore': true })), // Additional optional arguments }; - -export { PARAMETERS, ARGUMENTS }; diff --git a/packages/execution/test/utils/fixtures/index.ts b/packages/execution/test/utils/fixtures/index.ts new file mode 100644 index 00000000..20dc19db --- /dev/null +++ b/packages/execution/test/utils/fixtures/index.ts @@ -0,0 +1,4 @@ + +export * from './arguments.fixture'; +export * from './parameters.fixture'; +export * from './versions.fixture'; diff --git a/packages/execution/test/utils/fixtures/parameters.fixture.ts b/packages/execution/test/utils/fixtures/parameters.fixture.ts new file mode 100644 index 00000000..dac6634b --- /dev/null +++ b/packages/execution/test/utils/fixtures/parameters.fixture.ts @@ -0,0 +1,33 @@ + +import ArrayParameter from '../../../src/models/ArrayParameter'; +import NamedParameter from '../../../src/models/NamedParameter'; +import ObjectParameter from '../../../src/models/ObjectParameter'; + +export const PARAMETERS = +{ + NAMED: [new NamedParameter('id', false), new NamedParameter('name', false), new NamedParameter('age', true)], + ARRAY: [new ArrayParameter([new NamedParameter('query', false), new NamedParameter('sort', true)])], + OBJECT: [new ObjectParameter([new NamedParameter('query', false), new NamedParameter('sort', true)])], + MIXED: [ + new NamedParameter('id', false), + new ArrayParameter([new NamedParameter('name', false), new NamedParameter('age', true)]), + new ObjectParameter([new NamedParameter('query', false), new NamedParameter('sort', true)]) + ], + NESTED_ARRAY: [ + new ArrayParameter([ + new NamedParameter('id', false), + new ArrayParameter([new NamedParameter('name', false), new NamedParameter('age', true)]), + new ObjectParameter([new NamedParameter('query', false), new NamedParameter('sort', false)], undefined, true) + ]) + ], + NESTED_OBJECT: [ + new ObjectParameter([ + new NamedParameter('id', false), + new ArrayParameter([new NamedParameter('name', false), new NamedParameter('age', true)], 'person'), + new ObjectParameter([new NamedParameter('query', false), new NamedParameter('sort', false)], 'filter', true) + ]) + ], + REST: [new NamedParameter('...rest', false)], + REST_ARRAY: [new ArrayParameter([new NamedParameter('name', false), new NamedParameter('...rest', true)])], + REST_OBJECT: [new ObjectParameter([new NamedParameter('name', false), new NamedParameter('...rest', true)])] +}; diff --git a/packages/execution/test/utils/fixtures/versions.fixture.ts b/packages/execution/test/utils/fixtures/versions.fixture.ts new file mode 100644 index 00000000..ef7b56de --- /dev/null +++ b/packages/execution/test/utils/fixtures/versions.fixture.ts @@ -0,0 +1,13 @@ + +import Version from '../../../src/models/Version'; + +export const VERSIONS = +{ + DEFAULT: Version.DEFAULT, + ACTUAL: new Version(1, 2, 3), + EQUAL: new Version(1, 2, 3), + GREATER: new Version(10, 2, 3), + LESSER: new Version(1, 1, 3), + MAJOR: new Version(1, 0, 0), + MAJOR_MINOR: new Version(1, 2, 0) +}; diff --git a/packages/execution/tsconfig.json b/packages/execution/tsconfig.json new file mode 100644 index 00000000..e66fac12 --- /dev/null +++ b/packages/execution/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "rootDir": "./src/", + "moduleResolution": "node", + "declaration": true, + "outDir": "./dist", + "removeComments": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "skipLibCheck": true + }, + "exclude": [ + "vite.config.ts", + "node_modules", + "dist", + "test" + ] +} \ No newline at end of file diff --git a/packages/execution/vite.config.ts b/packages/execution/vite.config.ts new file mode 100644 index 00000000..f5182e1f --- /dev/null +++ b/packages/execution/vite.config.ts @@ -0,0 +1,10 @@ +// vite.config.ts +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + coverage: { + provider: 'v8' + }, + }, +}); diff --git a/packages/health/CHANGELOG.md b/packages/health/CHANGELOG.md new file mode 100644 index 00000000..80f29432 --- /dev/null +++ b/packages/health/CHANGELOG.md @@ -0,0 +1,4 @@ + +# Changelog + +This package doesn't keep a changelog. See the changelog in the [github repository](https://github.com/MaskingTechnology/jitar/blob/main/CHANGELOG.md) \ No newline at end of file diff --git a/packages/health/README.md b/packages/health/README.md new file mode 100644 index 00000000..1b3b99af --- /dev/null +++ b/packages/health/README.md @@ -0,0 +1,9 @@ + +# Jitar Health + +This package provides the service health system for the [Jitar](https://jitar.dev) runtime. + +For more information about Jitar: + +* [Visit our website](https://jitar.dev) +* [Read the documentation](https://docs.jitar.dev). diff --git a/packages/health/package.json b/packages/health/package.json new file mode 100644 index 00000000..aa5a4c1d --- /dev/null +++ b/packages/health/package.json @@ -0,0 +1,21 @@ +{ + "name": "@jitar/health", + "version": "0.7.4", + "description": "Health library for the Jitar runtime.", + "author": "Masking Technology (https://jitar.dev)", + "license": "MIT", + "type": "module", + "types": "dist/index.d.ts", + "exports": "./dist/index.js", + "private": true, + "scripts": { + "test": "vitest run", + "test-coverage": "vitest run --coverage", + "lint": "eslint . --ext .ts", + "build": "tsc -p tsconfig.json", + "clean": "rm -rf dist" + }, + "dependencies": { + "@jitar/errors": "*" + } +} diff --git a/packages/runtime/src/services/Runtime.ts b/packages/health/src/HealthManager.ts similarity index 59% rename from packages/runtime/src/services/Runtime.ts rename to packages/health/src/HealthManager.ts index 3a0774ee..932c737b 100644 --- a/packages/runtime/src/services/Runtime.ts +++ b/packages/health/src/HealthManager.ts @@ -1,42 +1,24 @@ -import { ExecutionScope, ExecutionScopes } from '../definitions/ExecutionScope.js'; -import InvalidHealthCheck from '../errors/InvalidHealthCheck.js'; -import HealthCheck from '../interfaces/HealthCheck.js'; -import Module from '../types/Module.js'; +import InvalidHealthCheck from './errors/InvalidHealthCheck'; +import type HealthCheck from './interfaces/HealthCheck'; -export default abstract class Runtime +export default class HealthManager { - #url?: string; - #healthCheckFiles: Set = new Set(); #healthChecks: Map = new Map(); - constructor(url?: string) - { - this.#url = url; - } - - get url() { return this.#url; } - - set healthCheckFiles(filenames: Set) - { - this.#healthCheckFiles = filenames; - } - - abstract import(url: string, scope: ExecutionScope): Promise; - - start(): Promise + addHealthCheck(healthCheck: HealthCheck): void { - return this.#importHealthChecks(); - } + if (healthCheck.isHealthy === undefined) + { + throw new InvalidHealthCheck(); + } - async stop(): Promise - { - this.#clearHealthChecks(); + this.#healthChecks.set(healthCheck.name, healthCheck); } - addHealthCheck(healthCheck: HealthCheck): void + clearHealthChecks(): void { - this.#healthChecks.set(healthCheck.name, healthCheck); + this.#healthChecks.clear(); } async isHealthy(): Promise @@ -80,32 +62,6 @@ export default abstract class Runtime .then(() => healthChecks); } - async #importHealthChecks(): Promise - { - for (const filename of this.#healthCheckFiles) - { - await this.#importHealthCheck(filename); - } - } - - async #importHealthCheck(url: string): Promise - { - const module = await this.import(url, ExecutionScopes.APPLICATION); - const healthCheck = module.default as HealthCheck; - - if (healthCheck?.isHealthy === undefined) - { - throw new InvalidHealthCheck(url); - } - - this.addHealthCheck(healthCheck as HealthCheck); - } - - #clearHealthChecks(): void - { - this.#healthChecks.clear(); - } - async #executeHealthCheck(healthCheck: HealthCheck): Promise { const health = healthCheck.isHealthy(); diff --git a/packages/health/src/errors/InvalidHealthCheck.ts b/packages/health/src/errors/InvalidHealthCheck.ts new file mode 100644 index 00000000..c3a671c7 --- /dev/null +++ b/packages/health/src/errors/InvalidHealthCheck.ts @@ -0,0 +1,10 @@ + +import { ServerError } from '@jitar/errors'; + +export default class InvalidHealthCheck extends ServerError +{ + constructor() + { + super('Invalid health check'); + } +} diff --git a/packages/health/src/index.ts b/packages/health/src/index.ts new file mode 100644 index 00000000..c2c87bcf --- /dev/null +++ b/packages/health/src/index.ts @@ -0,0 +1,4 @@ + +export { default as HealthCheck } from './interfaces/HealthCheck'; + +export { default as HealthManager } from './HealthManager'; diff --git a/packages/runtime/src/interfaces/HealthCheck.ts b/packages/health/src/interfaces/HealthCheck.ts similarity index 100% rename from packages/runtime/src/interfaces/HealthCheck.ts rename to packages/health/src/interfaces/HealthCheck.ts diff --git a/packages/runtime/test/services/Runtime.spec.ts b/packages/health/test/HealthManager.spec.ts similarity index 66% rename from packages/runtime/test/services/Runtime.spec.ts rename to packages/health/test/HealthManager.spec.ts index c2267e66..4af53aac 100644 --- a/packages/runtime/test/services/Runtime.spec.ts +++ b/packages/health/test/HealthManager.spec.ts @@ -1,49 +1,49 @@ import { describe, expect, it } from 'vitest'; -import { RUNTIMES } from '../_fixtures/services/Runtime.fixture'; +import { HEALTH_MANAGERS } from './fixtures'; -const goodRuntime = RUNTIMES.GOOD; -const badRuntime = RUNTIMES.BAD; -const errorRuntime = RUNTIMES.ERROR; -const timedOutRuntime = RUNTIMES.TIMEDOUT; -const inTimeRuntime = RUNTIMES.INTIME; +const errorManager = HEALTH_MANAGERS.ERROR; +const badManager = HEALTH_MANAGERS.BAD; +const goodManager = HEALTH_MANAGERS.GOOD; +const timedOutManager = HEALTH_MANAGERS.TIMEDOUT; +const inTimeManager = HEALTH_MANAGERS.INTIME; -describe('services/Runtime', () => +describe('HealthManager', () => { describe('.isHealthy()', () => { it('should be unhealthy when an error occurs', async () => { - const isHealthy = await errorRuntime.isHealthy(); + const isHealthy = await errorManager.isHealthy(); expect(isHealthy).toBeFalsy(); }); it('should be unhealthy with a bad health check', async () => { - const isHealthy = await badRuntime.isHealthy(); + const isHealthy = await badManager.isHealthy(); expect(isHealthy).toBeFalsy(); }); it('should be healthy with a good health check', async () => { - const isHealthy = await goodRuntime.isHealthy(); + const isHealthy = await goodManager.isHealthy(); expect(isHealthy).toBeTruthy(); }); it('should be unhealthy with a timed out health check', async () => { - const isHealthy = await timedOutRuntime.isHealthy(); + const isHealthy = await timedOutManager.isHealthy(); expect(isHealthy).toBeFalsy(); }); it('should be healthy with an in time health check', async () => { - const isHealthy = await inTimeRuntime.isHealthy(); + const isHealthy = await inTimeManager.isHealthy(); expect(isHealthy).toBeTruthy(); }); @@ -53,7 +53,7 @@ describe('services/Runtime', () => { it('should get a false state when an error occurs', async () => { - const health = await errorRuntime.getHealth(); + const health = await errorManager.getHealth(); const result = health.get('error'); expect(result).toBeFalsy(); @@ -61,7 +61,7 @@ describe('services/Runtime', () => it('should get a false state with a bad health check', async () => { - const health = await badRuntime.getHealth(); + const health = await badManager.getHealth(); const result = health.get('bad'); expect(result).toBeFalsy(); @@ -69,7 +69,7 @@ describe('services/Runtime', () => it('should get a true state with a good health check', async () => { - const health = await goodRuntime.getHealth(); + const health = await goodManager.getHealth(); const result = health.get('good'); expect(result).toBeTruthy(); @@ -77,7 +77,7 @@ describe('services/Runtime', () => it('should get a false state with a timed out health check', async () => { - const health = await timedOutRuntime.getHealth(); + const health = await timedOutManager.getHealth(); const result = health.get('timedOut'); expect(result).toBeFalsy(); @@ -85,7 +85,7 @@ describe('services/Runtime', () => it('should get a true state with an in time health check', async () => { - const health = await inTimeRuntime.getHealth(); + const health = await inTimeManager.getHealth(); const result = health.get('inTime'); expect(result).toBeTruthy(); diff --git a/packages/runtime/test/_fixtures/interfaces/HealthCheck.fixture.ts b/packages/health/test/fixtures/healthChecks.fixture.ts similarity index 92% rename from packages/runtime/test/_fixtures/interfaces/HealthCheck.fixture.ts rename to packages/health/test/fixtures/healthChecks.fixture.ts index c438fb1d..c8fd0dc5 100644 --- a/packages/runtime/test/_fixtures/interfaces/HealthCheck.fixture.ts +++ b/packages/health/test/fixtures/healthChecks.fixture.ts @@ -1,5 +1,5 @@ -import HealthCheck from '../../../src/interfaces/HealthCheck'; +import HealthCheck from '../../src/interfaces/HealthCheck'; class HealthyCheck implements HealthCheck { @@ -58,7 +58,7 @@ class InTimeHealthCheck implements HealthCheck } } -const HEALTH_CHECKS = +export const HEALTH_CHECKS = { GOOD: new HealthyCheck(), BAD: new UnhealthyCheck(), @@ -66,5 +66,3 @@ const HEALTH_CHECKS = TIMEDOUT: new TimedOutHealthCheck(), INTIME: new InTimeHealthCheck() }; - -export { HEALTH_CHECKS }; diff --git a/packages/health/test/fixtures/healthManagers.fixture.ts b/packages/health/test/fixtures/healthManagers.fixture.ts new file mode 100644 index 00000000..ea01b14d --- /dev/null +++ b/packages/health/test/fixtures/healthManagers.fixture.ts @@ -0,0 +1,28 @@ + +import HealthManager from '../../src/HealthManager'; + +import { HEALTH_CHECKS } from './healthChecks.fixture'; + +const goodManager = new HealthManager(); +goodManager.addHealthCheck(HEALTH_CHECKS.GOOD); + +const badManager = new HealthManager(); +badManager.addHealthCheck(HEALTH_CHECKS.BAD); + +const errorManager = new HealthManager(); +errorManager.addHealthCheck(HEALTH_CHECKS.ERROR); + +const timeoutManager = new HealthManager(); +timeoutManager.addHealthCheck(HEALTH_CHECKS.TIMEDOUT); + +const inTimeManager = new HealthManager(); +inTimeManager.addHealthCheck(HEALTH_CHECKS.INTIME); + +export const HEALTH_MANAGERS = +{ + GOOD: goodManager, + BAD: badManager, + ERROR: errorManager, + TIMEDOUT: timeoutManager, + INTIME: inTimeManager +}; diff --git a/packages/health/test/fixtures/index.ts b/packages/health/test/fixtures/index.ts new file mode 100644 index 00000000..ed93bc7d --- /dev/null +++ b/packages/health/test/fixtures/index.ts @@ -0,0 +1,3 @@ + +export * from './healthChecks.fixture'; +export * from './healthManagers.fixture'; diff --git a/packages/health/tsconfig.json b/packages/health/tsconfig.json new file mode 100644 index 00000000..e66fac12 --- /dev/null +++ b/packages/health/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "rootDir": "./src/", + "moduleResolution": "node", + "declaration": true, + "outDir": "./dist", + "removeComments": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "skipLibCheck": true + }, + "exclude": [ + "vite.config.ts", + "node_modules", + "dist", + "test" + ] +} \ No newline at end of file diff --git a/packages/health/vite.config.ts b/packages/health/vite.config.ts new file mode 100644 index 00000000..f5182e1f --- /dev/null +++ b/packages/health/vite.config.ts @@ -0,0 +1,10 @@ +// vite.config.ts +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + coverage: { + provider: 'v8' + }, + }, +}); diff --git a/packages/http/CHANGELOG.md b/packages/http/CHANGELOG.md new file mode 100644 index 00000000..80f29432 --- /dev/null +++ b/packages/http/CHANGELOG.md @@ -0,0 +1,4 @@ + +# Changelog + +This package doesn't keep a changelog. See the changelog in the [github repository](https://github.com/MaskingTechnology/jitar/blob/main/CHANGELOG.md) \ No newline at end of file diff --git a/packages/http/README.md b/packages/http/README.md new file mode 100644 index 00000000..077ac036 --- /dev/null +++ b/packages/http/README.md @@ -0,0 +1,9 @@ + +# Jitar Execution + +This package provides the HTTP communication for the [Jitar](https://jitar.dev) runtime. + +For more information about Jitar: + +* [Visit our website](https://jitar.dev) +* [Read the documentation](https://docs.jitar.dev). diff --git a/packages/http/package.json b/packages/http/package.json new file mode 100644 index 00000000..b06429ea --- /dev/null +++ b/packages/http/package.json @@ -0,0 +1,25 @@ +{ + "name": "@jitar/http", + "version": "0.7.4", + "description": "HTTP implementations library for the Jitar runtime.", + "author": "Masking Technology (https://jitar.dev)", + "license": "MIT", + "type": "module", + "types": "dist/index.d.ts", + "exports": "./dist/index.js", + "private": true, + "scripts": { + "test": "vitest run", + "test-coverage": "vitest run --coverage", + "lint": "eslint . --ext .ts", + "build": "tsc -p tsconfig.json", + "clean": "rm -rf dist" + }, + "dependencies": { + "@jitar/errors": "*", + "@jitar/execution": "*", + "@jitar/runtime": "*", + "@jitar/services": "*", + "@jitar/validation": "*" + } +} diff --git a/packages/http/src/HttpRemote.ts b/packages/http/src/HttpRemote.ts new file mode 100644 index 00000000..86e2453e --- /dev/null +++ b/packages/http/src/HttpRemote.ts @@ -0,0 +1,178 @@ + +import { ErrorConverter, Request, Response as ResultResponse } from '@jitar/execution'; +import { Remote } from '@jitar/services'; +import { File } from '@jitar/sourcing'; + +import HeaderKeys from './definitions/HeaderKeys'; +import HeaderValues from './definitions/HeaderValues'; + +export default class HttpRemote implements Remote +{ + #url: string; + + #errorConverter = new ErrorConverter(); + + constructor(url: string) + { + this.#url = url; + } + + connect(): Promise + { + return Promise.resolve(); + } + + disconnect(): Promise + { + return Promise.resolve(); + } + + async provide(filename: string): Promise + { + const remoteUrl = `${this.#url}/${filename}`; + const options = { method: 'GET' }; + + const response = await this.#callRemote(remoteUrl, options); + const type = response.headers.get(HeaderKeys.CONTENT_TYPE) ?? HeaderValues.APPLICATION_STREAM; + const result = await response.arrayBuffer(); + const content = Buffer.from(result); + + return new File(filename, type, content); + } + + async isHealthy(): Promise + { + const remoteUrl = `${this.#url}/health/status`; + const options = { method: 'GET' }; + + const response = await this.#callRemote(remoteUrl, options); + const healthy = await response.text(); + + return Boolean(healthy); + } + + async getHealth(): Promise> + { + const remoteUrl = `${this.#url}/health`; + const options = { method: 'GET' }; + + const response = await this.#callRemote(remoteUrl, options); + const health = await response.json(); + + return new Map(Object.entries(health)); + } + + async addWorker(url: string, procedureNames: string[], trustKey?: string): Promise + { + const remoteUrl = `${this.#url}/workers`; + const body = { url, procedureNames, trustKey }; + const options = + { + method: 'POST', + headers: { 'Content-Type': HeaderValues.APPLICATION_JSON }, + body: JSON.stringify(body) + }; + + await this.#callRemote(remoteUrl, options); + } + + async run(request: Request): Promise + { + request.setHeader(HeaderKeys.CONTENT_TYPE, HeaderValues.APPLICATION_JSON); + + const argsObject = Object.fromEntries(request.args); + const headersObject = Object.fromEntries(request.headers); + + const versionString = request.version.toString(); + headersObject[HeaderKeys.JITAR_PROCEDURE_VERSION] = versionString; + + const remoteUrl = `${this.#url}/rpc/${request.fqn}`; + const body = await this.#createRequestBody(argsObject); + const options = + { + method: 'POST', + redirect: 'manual', + headers: headersObject, + body: body + }; + + const response = await this.#callRemote(remoteUrl, options, false); + const status = response.status; + + const result = await this.#getResponseResult(response); + const headers = this.#createResponseHeaders(response); + + return new ResultResponse(status, result, headers); + } + + async #callRemote(remoteUrl: string, options: object, throwOnError = true): Promise + { + const response = await fetch(remoteUrl, options); + + if (throwOnError && this.#isErrorResponse(response)) + { + const result = await this.#getResponseResult(response); + + throw this.#errorConverter.fromStatus(response.status, String(result)); + } + + return response; + } + + #isErrorResponse(response: Response): boolean + { + return response.status < 200 || response.status > 399; + } + + async #createRequestBody(body: unknown): Promise + { + return JSON.stringify(body); + } + + async #getResponseResult(response: Response): Promise + { + const contentType = response.headers.get(HeaderKeys.JITAR_CONTENT_TYPE) + ?? response.headers.get(HeaderKeys.CONTENT_TYPE); + + if (contentType?.includes('undefined')) + { + return undefined; + } + + if (contentType?.includes('null')) + { + return null; + } + + if (contentType?.includes('json')) + { + return response.json(); + } + + const content = await response.text(); + + if (contentType?.includes('boolean')) + { + return content === 'true'; + } + + if (contentType?.includes('number')) + { + return Number(content); + } + + return content; + } + + #createResponseHeaders(response: Response): Map + { + const headers = new Map(); + + for (const [name, value] of response.headers) + { + headers.set(name, value); + } + + return headers; + } +} diff --git a/packages/http/src/HttpRemoteBuilder.ts b/packages/http/src/HttpRemoteBuilder.ts new file mode 100644 index 00000000..2e25f7fc --- /dev/null +++ b/packages/http/src/HttpRemoteBuilder.ts @@ -0,0 +1,12 @@ + +import type { Remote, RemoteBuilder } from '@jitar/services'; + +import HttpRemote from './HttpRemote'; + +export default class HttpRemoteBuilder implements RemoteBuilder +{ + build(url: string): Remote + { + return new HttpRemote(url); + } +} diff --git a/packages/http/src/HttpServer.ts b/packages/http/src/HttpServer.ts new file mode 100644 index 00000000..b5d0eba9 --- /dev/null +++ b/packages/http/src/HttpServer.ts @@ -0,0 +1,318 @@ + +import express, { Express, Request, Response, NextFunction } from 'express'; +import { Server as Http } from 'http'; + +import { RunModes } from '@jitar/execution'; +import { Server, ServerResponse, ContentTypes } from '@jitar/runtime'; +import { Validator } from '@jitar/validation'; + +import Defaults from './definitions/Defaults'; +import HeaderKeys from './definitions/HeaderKeys'; +import IgnoredHeaderKeys from './definitions/IgnoredHeaderKeys'; +import HeaderValues from './definitions/HeaderValues'; + +export default class HttpServer +{ + #server: Server; + #port: string; + #app: Express; + #http?: Http; + + #validator = new Validator(); + + constructor(server: Server, port: string = Defaults.PORT_NUMBER, bodyLimit: number = Defaults.BODY_LIMIT) + { + this.#server = server; + this.#port = port; + this.#app = express(); + + this.#setupExpress(bodyLimit); + this.#setupRoutes(); + } + + async start(): Promise + { + await this.#server.start(); + + return this.#startHttp(); + } + + async stop(): Promise + { + await this.#stopHttp(); + + return this.#server.stop(); + } + + #setupExpress(bodyLimit: number): void + { + this.#app.use(express.json({limit: bodyLimit })); + this.#app.use(express.urlencoded({ extended: true })); + this.#app.use(this.#addDefaultHeaders.bind(this)); + + this.#app.disable(HeaderKeys.POWERED_BY); + } + + #setupRoutes(): void + { + this.#app.get('/health', this.#getHealth.bind(this)); + this.#app.get('/health/status', this.#isHealthy.bind(this)); + + this.#app.get('/rpc/*', this.#runGet.bind(this)); + this.#app.post('/rpc/*', this.#runPost.bind(this)); + this.#app.options('/rpc/*', this.#runOptions.bind(this)); + + this.#app.post('/workers', this.#addWorker.bind(this)); + + this.#app.get('*', this.#provide.bind(this)); + } + + #startHttp(): Promise + { + if (this.#http !== undefined) + { + return Promise.resolve(); + } + + return new Promise((resolve, reject) => + { + this.#http = this.#app.listen(this.#port, resolve); + + this.#http.on('error', reject); + }); + } + + #stopHttp(): Promise + { + if (this.#http === undefined) + { + return Promise.resolve(); + } + + return new Promise(resolve => { this.#http!.close(() => resolve()); }); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + #addDefaultHeaders(request: Request, response: Response, next: NextFunction): void + { + response.setHeader(HeaderKeys.CONTENT_TYPE_OPTIONS, HeaderValues.NO_SNIFF); + + next(); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + async #getHealth(request: Request, response: Response): Promise + { + const serverResponse = await this.#server.getHealth(); + + return this.#transformResponse(response, serverResponse); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + async #isHealthy(request: Request, response: Response): Promise + { + const serverResponse = await this.#server.isHealthy(); + + return this.#transformResponse(response, serverResponse); + } + + async #runGet(request: Request, response: Response): Promise + { + const fqn = this.#extractFqn(request); + const version = this.#extractVersion(request); + const args = this.#extractQueryArguments(request); + const headers = this.#extractHeaders(request); + const mode = RunModes.NORMAL; + + const serverResponse = await this.#server.run({ fqn, version, args, headers, mode }); + + return this.#transformResponse(response, serverResponse); + } + + async #runPost(request: Request, response: Response): Promise + { + const fqn = this.#extractFqn(request); + const version = this.#extractVersion(request); + const args = this.#extractBodyArguments(request); + const headers = this.#extractHeaders(request); + const mode = RunModes.NORMAL; + + const serverResponse = await this.#server.run({ fqn, version, args, headers, mode }); + + return this.#transformResponse(response, serverResponse); + } + + async #runOptions(request: Request, response: Response): Promise + { + // Perform a dry run + + const fqn = this.#extractFqn(request); + const version = this.#extractVersion(request); + const args = this.#extractBodyArguments(request); + const headers = this.#extractHeaders(request); + const mode = RunModes.DRY; + + const serverResponse = await this.#server.run({ fqn, version, args, headers, mode }); + + return this.#transformResponse(response, serverResponse); + } + + async #addWorker(request: Request, response: Response): Promise + { + const args = this.#extractBodyArguments(request); + + const validation = this.#validator.validate(args, + { + url: { type: 'url', required: true }, + procedureNames: { type: 'list', required: true, items: { type: 'string' } }, + trustKey: { type: 'string', required: false } + }); + + if (validation.valid === false) + { + return response.status(400).send(validation.errors.join('\n')); + } + + const url = args.url as string; + const procedureNames = args.procedureNames as string[]; + const trustKey = args.trustKey as string | undefined; + + try + { + const serverResponse = await this.#server.addWorker({ url, procedureNames, trustKey }); + + return this.#transformResponse(response, serverResponse); + } + catch (error: unknown) + { + const message = error instanceof Error ? error.message : 'Server error'; + + return response.status(500).send(message); + } + } + + async #provide(request: Request, response: Response): Promise + { + const path = request.path.substring(1).trim(); + const filename = decodeURIComponent(path); + + const serverResponse = await this.#server.provide({ filename }); + + return this.#transformResponse(response, serverResponse); + } + + #extractFqn(request: Request): string + { + const decodedFqn = decodeURIComponent(request.path.trim()); + + return decodedFqn.substring(5).trim(); + } + + #extractVersion(request: Request): string | undefined + { + const versionString = request.headers[HeaderKeys.JITAR_PROCEDURE_VERSION]; + + return Array.isArray(versionString) ? versionString[0] : versionString; + } + + #extractQueryArguments(request: Request): Record + { + const args: Record = {}; + + for (const [key, value] of Object.entries(request.query)) + { + args[key] = value; + } + + return args; + } + + #extractBodyArguments(request: Request): Record + { + return request.body; + } + + #extractHeaders(request: Request): Record + { + const headers: Record = {}; + + for (const [key, value] of Object.entries(request.headers)) + { + if (value === undefined) continue; + + const lowerKey = key.toLowerCase(); + const stringValue = value.toString(); + + if (IgnoredHeaderKeys.includes(lowerKey)) + { + continue; + } + + headers[lowerKey] = stringValue; + } + + return headers; + } + + #transformResponse(response: Response, serverResponse: ServerResponse): Response + { + const status = this.#transformStatus(serverResponse); + const contentType = this.#transformContentType(serverResponse); + + response.status(status); + response.setHeader(HeaderKeys.CONTENT_TYPE, contentType); + response.setHeader(HeaderKeys.JITAR_CONTENT_TYPE, serverResponse.contentType); + + for (const [name, value] of Object.entries(serverResponse.headers)) + { + response.setHeader(name, value); + } + + const result = this.#transformResult(serverResponse); + + return response.send(result); + } + + #transformStatus(serverResponse: ServerResponse): number + { + if (serverResponse.headers.location !== undefined) + { + return 302; + } + + return serverResponse.status; + } + + #transformContentType(serverResponse: ServerResponse): string + { + const contentType = serverResponse.contentType.toLowerCase(); + + switch (contentType) + { + case ContentTypes.BOOLEAN: + case ContentTypes.NUMBER: + case ContentTypes.UNDEFINED: + case ContentTypes.NULL: + return ContentTypes.TEXT; + } + + return contentType; + } + + #transformResult(serverResponse: ServerResponse): unknown + { + const result = serverResponse.result; + + // if (result === undefined || result === null) + // { + // return ''; + // } + + if (typeof result === 'number' || typeof result === 'boolean') + { + return String(result); + } + + return result; + } +} diff --git a/packages/http/src/definitions/Defaults.ts b/packages/http/src/definitions/Defaults.ts new file mode 100644 index 00000000..0e8fec27 --- /dev/null +++ b/packages/http/src/definitions/Defaults.ts @@ -0,0 +1,8 @@ + +const Defaults = +{ + PORT_NUMBER: '3000', + BODY_LIMIT: 1024 * 200 // 200 KB +} as const; + +export default Defaults; diff --git a/packages/http/src/definitions/HeaderKeys.ts b/packages/http/src/definitions/HeaderKeys.ts new file mode 100644 index 00000000..c1ad2a35 --- /dev/null +++ b/packages/http/src/definitions/HeaderKeys.ts @@ -0,0 +1,12 @@ + +const HeaderKeys = +{ + CONTENT_TYPE: 'content-type', + CONTENT_LENGTH: 'content-length', + CONTENT_TYPE_OPTIONS: 'x-content-type-options', + JITAR_CONTENT_TYPE: 'x-jitar-content-type', + JITAR_PROCEDURE_VERSION: 'x-jitar-procedure-version', + POWERED_BY: 'x-powered-by' +} as const; + +export default HeaderKeys; diff --git a/packages/http/src/definitions/HeaderValues.ts b/packages/http/src/definitions/HeaderValues.ts new file mode 100644 index 00000000..035c6f7d --- /dev/null +++ b/packages/http/src/definitions/HeaderValues.ts @@ -0,0 +1,9 @@ + +const HeaderValues = +{ + NO_SNIFF: 'nosniff', + APPLICATION_JSON: 'application/json', + APPLICATION_STREAM: 'application/octet-stream' +} as const; + +export default HeaderValues; diff --git a/packages/http/src/definitions/IgnoredHeaderKeys.ts b/packages/http/src/definitions/IgnoredHeaderKeys.ts new file mode 100644 index 00000000..45131545 --- /dev/null +++ b/packages/http/src/definitions/IgnoredHeaderKeys.ts @@ -0,0 +1,4 @@ + +const IgnoredHeaderKeys: string[] = ['host', 'connection', 'content-length', 'accept-encoding', 'user-agent', 'keep-alive'] as const; + +export default IgnoredHeaderKeys; diff --git a/packages/http/src/index.ts b/packages/http/src/index.ts new file mode 100644 index 00000000..2da205eb --- /dev/null +++ b/packages/http/src/index.ts @@ -0,0 +1,6 @@ + +export { default as CorsMiddleware } from './middleware/CorsMiddleware'; + +export { default as HttpRemote } from './HttpRemote'; +export { default as HttpRemoteBuilder } from './HttpRemoteBuilder'; +export { default as HttpServer } from './HttpServer'; diff --git a/packages/server-nodejs/src/middleware/CorsMiddleware.ts b/packages/http/src/middleware/CorsMiddleware.ts similarity index 88% rename from packages/server-nodejs/src/middleware/CorsMiddleware.ts rename to packages/http/src/middleware/CorsMiddleware.ts index 6dec1bc9..a9f7a93e 100644 --- a/packages/server-nodejs/src/middleware/CorsMiddleware.ts +++ b/packages/http/src/middleware/CorsMiddleware.ts @@ -1,5 +1,6 @@ -import { Middleware, NextHandler, Request, Response } from '@jitar/runtime'; +import { Request, Response } from '@jitar/execution'; +import { Middleware, NextHandler } from '@jitar/middleware'; export default class CorsMiddleware implements Middleware { diff --git a/packages/http/test/dummy.spec.ts b/packages/http/test/dummy.spec.ts new file mode 100644 index 00000000..2489f1ee --- /dev/null +++ b/packages/http/test/dummy.spec.ts @@ -0,0 +1,12 @@ + +import { describe, expect, it } from 'vitest'; + +describe('dummy', () => +{ + // TODO: Add real tests + + it('should not complain about missing tests', async () => + { + expect(true).toBeTruthy(); + }); +}); diff --git a/packages/http/tsconfig.json b/packages/http/tsconfig.json new file mode 100644 index 00000000..307c6271 --- /dev/null +++ b/packages/http/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "rootDir": "./src/", + "moduleResolution": "node", + "declaration": true, + "outDir": "./dist", + "removeComments": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "skipLibCheck": true + }, + "dependencies": { + "express": "^4.19.2", + }, + "exclude": [ + "vite.config.ts", + "node_modules", + "dist", + "test" + ] +} \ No newline at end of file diff --git a/packages/http/vite.config.ts b/packages/http/vite.config.ts new file mode 100644 index 00000000..f5182e1f --- /dev/null +++ b/packages/http/vite.config.ts @@ -0,0 +1,10 @@ +// vite.config.ts +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + coverage: { + provider: 'v8' + }, + }, +}); diff --git a/packages/jitar/README.md b/packages/jitar/README.md index 50c61921..5f7604e3 100644 --- a/packages/jitar/README.md +++ b/packages/jitar/README.md @@ -3,10 +3,10 @@ [Jitar](https://jitar.dev) is a distributed runtime for JavaScript and TypeScript applications. -To add Jitar to your project run: +To install Jitar, just run: ```bash -npm install jitar +npm install -g jitar ``` For more information about Jitar: diff --git a/packages/jitar/package.json b/packages/jitar/package.json index 9f3da010..af6e1fc0 100644 --- a/packages/jitar/package.json +++ b/packages/jitar/package.json @@ -5,11 +5,10 @@ "author": "Masking Technology (https://jitar.dev)", "license": "MIT", "type": "module", - "types": "./dist/lib.d.ts", + "types": "./dist/types/lib.d.ts", "sideEffects": true, "exports": { ".": "./dist/lib.js", - "./server": "./dist/server.js", "./client": "./dist/client.js" }, "files": [ @@ -18,26 +17,29 @@ "dist", "!dist/types" ], + "bin": "./dist/cli.js", "scripts": { "lint": "eslint . --ext .ts", "validate": "tsc -p tsconfig.json --noEmit", - "build": "npm run clean && rollup -c", + "build": "npm run clean && rollup -c && chmod +x dist/cli.js", "clean": "rm -rf dist", "prepublishOnly": "npm run clean && npm run build" }, "dependencies": { + "dotenv": "^16.4.5", "express": "^4.19.2", - "express-http-proxy": "^2.0.0", "fs-extra": "^11.2.0", - "glob": "10.4.3", - "mime-types": "^2.1.35", - "tslog": "^4.9.3", - "yargs": "^17.7.2", - "zod": "^3.23.8" + "glob": "11.0.0", + "mime-types": "^2.1.35" }, "devDependencies": { - "@jitar/runtime": "*", - "@jitar/server-nodejs": "*" + "@jitar/cli": "*", + "@jitar/errors": "*", + "@jitar/execution": "*", + "@jitar/health": "*", + "@jitar/logging": "*", + "@jitar/middleware": "*", + "@jitar/http": "*" }, "engines": { "node": ">=20.0" diff --git a/packages/jitar/rollup.config.js b/packages/jitar/rollup.config.js index ed8c5b69..6a7df429 100644 --- a/packages/jitar/rollup.config.js +++ b/packages/jitar/rollup.config.js @@ -2,20 +2,19 @@ import terser from '@rollup/plugin-terser'; import { nodeResolve } from '@rollup/plugin-node-resolve'; import typescript from '@rollup/plugin-typescript'; -import replace from '@rollup/plugin-replace'; -import dts from 'rollup-plugin-dts'; -import { SERVER_EXTERNALS, REPLACE_VALUES } from './rollup.definitions.js'; +import { SERVER_EXTERNALS } from './rollup.definitions.js'; -export default [ - { +function bundle(input, output, supportBrowser) +{ + return { external: SERVER_EXTERNALS, - input: { - server: 'src/server.ts', - client: 'src/client.ts' + treeshake: { + moduleSideEffects: false }, + input, output: { - dir: 'dist', + ...output, exports: 'named', format: 'module', plugins: [terser({ @@ -25,32 +24,14 @@ export default [ }, plugins: [ typescript(), - replace({ - preventAssignment: true, - values: REPLACE_VALUES - }), - nodeResolve() + nodeResolve({ + browser: supportBrowser + }) ] - }, - { - external: [ - './client.js', - './server.js' - ], - input: 'src/lib.ts', - output: { - file: 'dist/lib.js', - format: 'module' - }, - plugins: [ - typescript() - ] - }, - { - input: './dist/types/lib.d.ts', - output: [{ file: 'dist/lib.d.ts', format: 'module' }], - plugins: [dts({ - respectExternal: true - })], - } -] + }; +} + +export default [ + bundle(['src/cli.ts', 'src/lib.ts'], { dir: 'dist' }, false), + bundle('src/client.ts', { file: 'dist/client.js' }, true) +]; diff --git a/packages/jitar/rollup.definitions.js b/packages/jitar/rollup.definitions.js index f9d1987c..01e8cd4d 100644 --- a/packages/jitar/rollup.definitions.js +++ b/packages/jitar/rollup.definitions.js @@ -2,18 +2,8 @@ export const SERVER_EXTERNALS = [ 'express', - 'express-http-proxy', 'fs-extra', 'glob', 'mime-types', - 'tslog', - 'yargs', - 'zod' + 'dotenv' ]; - -export const REPLACE_VALUES = -{ - 'RUNTIME_ERROR_LOCATION': '/jitar/client.js', - 'RUNTIME_HOOKS_LOCATION': '/jitar/client.js', - 'JITAR_LIBRARY_NAME': 'jitar', -}; diff --git a/packages/jitar/src/cli.ts b/packages/jitar/src/cli.ts new file mode 100755 index 00000000..ed995051 --- /dev/null +++ b/packages/jitar/src/cli.ts @@ -0,0 +1,18 @@ +#!/usr/bin/env node + +import { Cli } from '@jitar/cli'; +import { Logger } from '@jitar/logging'; + +try +{ + const cli = new Cli(); + + await cli.start(); +} +catch (error: unknown) +{ + const logger = new Logger(); + const message = error instanceof Error ? error.message : String(error); + + logger.fatal(message); +} diff --git a/packages/jitar/src/client.ts b/packages/jitar/src/client.ts index 65abe637..2ffe1c51 100644 --- a/packages/jitar/src/client.ts +++ b/packages/jitar/src/client.ts @@ -1,43 +1,6 @@ -export -{ - HealthCheck, - Middleware, - NextHandler, - Request, - Response, - Segment, - Procedure, - Implementation, - Version, - NamedParameter, - ArrayParameter, - ObjectParameter, - BadRequest, - Forbidden, - NotFound, - NotImplemented, - PaymentRequired, - ServerError, - Teapot, - Unauthorized, - ClientNotFound, - FileNotFound, - ImplementationNotFound, - InvalidClientId, - InvalidParameterValue, - InvalidSegmentFile, - InvalidVersionNumber, - MissingParameterValue, - ModuleNotAccessible, - ModuleNotLoaded, - NoWorkerAvailable, - ProcedureNotAccessible, - ProcedureNotFound, - RepositoryNotAvailable, - RuntimeNotAvailable, - SegmentNotFound, - UnknownParameter, - startClient, - getClient -} from '@jitar/runtime'; +export { BadRequest, Forbidden, NotFound, NotImplemented, PaymentRequired, ServerError, Teapot, Unauthorized } from '@jitar/errors'; +export { Request, Response, Segment, Class, Procedure, Implementation, Version, NamedParameter, ArrayParameter, ObjectParameter } from '@jitar/execution'; +export { Middleware, NextHandler } from '@jitar/middleware'; +export { ClientBuilder } from '@jitar/runtime'; +export { HttpRemoteBuilder } from '@jitar/http'; diff --git a/packages/jitar/src/lib.ts b/packages/jitar/src/lib.ts index 17faeca0..8245e9a6 100644 --- a/packages/jitar/src/lib.ts +++ b/packages/jitar/src/lib.ts @@ -1,3 +1,6 @@ -export * from './client.js'; -export * from './server.js'; +export { BadRequest, Forbidden, NotFound, NotImplemented, PaymentRequired, ServerError, Teapot, Unauthorized } from '@jitar/errors'; +export { Request, Response, Segment, Class, Procedure, Implementation, Version, NamedParameter, ArrayParameter, ObjectParameter } from '@jitar/execution'; +export { Middleware, NextHandler } from '@jitar/middleware'; +export { HealthCheck } from '@jitar/health'; +export { CorsMiddleware } from '@jitar/http'; diff --git a/packages/jitar/src/server.ts b/packages/jitar/src/server.ts deleted file mode 100644 index abfd3492..00000000 --- a/packages/jitar/src/server.ts +++ /dev/null @@ -1,2 +0,0 @@ - -export { buildServer, CorsMiddleware } from '@jitar/server-nodejs'; diff --git a/packages/logging/CHANGELOG.md b/packages/logging/CHANGELOG.md new file mode 100644 index 00000000..80f29432 --- /dev/null +++ b/packages/logging/CHANGELOG.md @@ -0,0 +1,4 @@ + +# Changelog + +This package doesn't keep a changelog. See the changelog in the [github repository](https://github.com/MaskingTechnology/jitar/blob/main/CHANGELOG.md) \ No newline at end of file diff --git a/packages/logging/README.md b/packages/logging/README.md new file mode 100644 index 00000000..743d3eed --- /dev/null +++ b/packages/logging/README.md @@ -0,0 +1,9 @@ + +# Jitar Logging + +This package provides logging for the [Jitar](https://jitar.dev) runtime. + +For more information about Jitar: + +* [Visit our website](https://jitar.dev) +* [Read the documentation](https://docs.jitar.dev). diff --git a/packages/logging/package.json b/packages/logging/package.json new file mode 100644 index 00000000..868a74c2 --- /dev/null +++ b/packages/logging/package.json @@ -0,0 +1,22 @@ +{ + "name": "@jitar/logging", + "version": "0.7.4", + "description": "Logging library for the Jitar runtime.", + "author": "Masking Technology (https://jitar.dev)", + "license": "MIT", + "type": "module", + "types": "dist/index.d.ts", + "exports": "./dist/index.js", + "private": true, + "scripts": { + "test": "vitest run", + "test-coverage": "vitest run --coverage", + "lint": "eslint . --ext .ts", + "build": "tsc -p tsconfig.json", + "clean": "rm -rf dist" + }, + "dependencies": { + "@jitar/errors": "*", + "@jitar/execution": "*" + } +} diff --git a/packages/logging/src/Logger.ts b/packages/logging/src/Logger.ts new file mode 100644 index 00000000..20b75d83 --- /dev/null +++ b/packages/logging/src/Logger.ts @@ -0,0 +1,106 @@ + +import type Writer from './Writer'; + +export default class Logger +{ + #debugEnabled: boolean; + #writer: Writer; + + constructor(debugEnabled: boolean = false, writer: Writer = console) + { + this.#debugEnabled = debugEnabled; + this.#writer = writer; + } + + info(...message: unknown[]): void + { + const messageString = this.#createMessage('[INFO]', ...message); + + this.#writer.info(messageString); + } + + warn(...message: unknown[]): void + { + const messageString = this.#createMessage('[WARN]', ...message); + + this.#writer.warn(messageString); + } + + error(...message: unknown[]): void + { + const messageString = this.#createMessage('[ERROR]', ...message); + + this.#writer.error(messageString); + } + + fatal(...message: unknown[]): void + { + const messageString = this.#createMessage('[FATAL]', ...message); + + this.#writer.error(messageString); + } + + debug(...message: unknown[]): void + { + if (this.#debugEnabled === false) + { + return; + } + + const messageString = this.#createMessage('[DEBUG]', ...message); + + this.#writer.debug(messageString); + } + + #createMessage(...message: unknown[]): string + { + return message.map(value => this.#interpretValue(value)).join(' '); + } + + #interpretValue(value: unknown, level: number = 0): string + { + let result: string; + + switch (typeof value) + { + case 'string': result = value; break; + case 'object': result = this.#interpretObject(value, level + 1); break; + case 'undefined': result = 'undefined'; break; + case 'function': result = 'function'; break; + default: result = String(value); break; + } + + const prefix = this.#indent(level); + + return `${prefix}${result}`; + } + + #interpretObject(object: unknown, level: number): string + { + if (object === null) + { + return 'null'; + } + + if (Array.isArray(object)) + { + const items = object.map(value => this.#interpretValue(value, level)).join(',\n'); + + const postfix = this.#indent(level - 1); + + return `[\n${items}\n${postfix}]`; + } + + if (object instanceof Error) + { + return object.stack ?? object.message; + } + + return JSON.stringify(object); + } + + #indent(level: number): string + { + return ' '.repeat(level); + } +} diff --git a/packages/logging/src/Writer.ts b/packages/logging/src/Writer.ts new file mode 100644 index 00000000..f8e6914b --- /dev/null +++ b/packages/logging/src/Writer.ts @@ -0,0 +1,11 @@ + +interface Writer +{ + log(message: string): void; + debug(message: string): void; + info(message: string): void; + warn(message: string): void; + error(message: string): void; +} + +export default Writer; diff --git a/packages/logging/src/index.ts b/packages/logging/src/index.ts new file mode 100644 index 00000000..b9f28f81 --- /dev/null +++ b/packages/logging/src/index.ts @@ -0,0 +1,3 @@ + +export { default as Logger } from './Logger'; +export { default as Writer } from './Writer'; diff --git a/packages/logging/test/Logger.spec.ts b/packages/logging/test/Logger.spec.ts new file mode 100644 index 00000000..7955bd61 --- /dev/null +++ b/packages/logging/test/Logger.spec.ts @@ -0,0 +1,138 @@ + +import { beforeEach, describe, expect, it } from 'vitest'; + +import { Logger } from '../src'; + +import { LOGGERS, writer, INPUT, OUTPUT } from './fixtures'; + +const normalLogger = LOGGERS.NORMAL; +const debugLogger = LOGGERS.DEBUG; + +beforeEach(() => +{ + writer.clear(); +}); + +describe('Logger', () => +{ + describe('Message creation', () => + { + const logger = new Logger(false, writer); + + it('should log [CATEGORY] messages', () => + { + normalLogger.info('info'); + normalLogger.warn('warn'); + normalLogger.error('error'); + normalLogger.fatal('fatal'); + + expect(writer.messages).toEqual([ + '[INFO] info', + '[WARN] warn', + '[ERROR] error', + '[FATAL] fatal' + ]); + }); + + it('should format string message', () => + { + normalLogger.info(INPUT.STRING); + + expect(writer.lastMessage).toEqual(OUTPUT.STRING); + }); + + it('should format number message', () => + { + normalLogger.info(INPUT.NUMBER); + + expect(writer.lastMessage).toEqual(OUTPUT.NUMBER); + }); + + it('should format boolean message', () => + { + normalLogger.info(INPUT.BOOLEAN); + + expect(writer.lastMessage).toEqual(OUTPUT.BOOLEAN); + }); + + it('should format object message', () => + { + normalLogger.info(INPUT.OBJECT); + + expect(writer.lastMessage).toEqual(OUTPUT.OBJECT); + }); + + it('should format array', () => + { + normalLogger.info(INPUT.ARRAY); + + expect(writer.lastMessage).toEqual(OUTPUT.ARRAY); + }); + + it('should format function', () => + { + normalLogger.info(INPUT.FUNCTION); + + expect(writer.lastMessage).toEqual(OUTPUT.FUNCTION); + }); + + it('should format undefined', () => + { + normalLogger.info(INPUT.UNDEFINED); + + expect(writer.lastMessage).toEqual(OUTPUT.UNDEFINED); + }); + + it('should format null', () => + { + normalLogger.info(INPUT.NULL); + + expect(writer.lastMessage).toEqual(OUTPUT.NULL); + }); + + it('should format nested object', () => + { + normalLogger.info(INPUT.NESTED_OBJECT); + + expect(writer.lastMessage).toEqual(OUTPUT.NESTED_OBJECT); + }); + + it('should format nested array', () => + { + normalLogger.info(INPUT.NESTED_ARRAY); + + expect(writer.lastMessage).toEqual(OUTPUT.NESTED_ARRAY); + }); + + it('should format error without stacktrace', () => + { + normalLogger.info(INPUT.ERROR_WITHOUT_STACKTRACE); + + expect(writer.lastMessage).toEqual(OUTPUT.ERROR_WITHOUT_STACKTRACE); + }); + + it('should format error with stacktrace', () => + { + logger.info(INPUT.ERROR_WITH_STACKTRACE); + + expect(writer.lastMessage).toEqual(OUTPUT.ERROR_WITH_STACKTRACE); + }); + }); + + describe('Debug mode', () => + { + it('should log messages when debug is enabled', () => + { + debugLogger.debug('message'); + + expect(writer.lastMessage).toEqual('[DEBUG] message'); + }); + + it('should not log messages when debug is disabled', () => + { + normalLogger.debug('message'); + + expect(writer.lastMessage).toEqual(undefined); + }); + }); +}); diff --git a/packages/logging/test/fixtures/index.ts b/packages/logging/test/fixtures/index.ts new file mode 100644 index 00000000..9b839ea9 --- /dev/null +++ b/packages/logging/test/fixtures/index.ts @@ -0,0 +1,4 @@ + +export * from './loggers.fixture'; +export * from './values.fixture'; +export * from './writer.fixture'; diff --git a/packages/logging/test/fixtures/loggers.fixture.ts b/packages/logging/test/fixtures/loggers.fixture.ts new file mode 100644 index 00000000..ac5e16ef --- /dev/null +++ b/packages/logging/test/fixtures/loggers.fixture.ts @@ -0,0 +1,13 @@ + +import Logger from '../../src/Logger'; + +import { writer } from './writer.fixture'; + +const normalLogger = new Logger(false, writer); +const debugLogger = new Logger(true, writer); + +export const LOGGERS = +{ + NORMAL: normalLogger, + DEBUG: debugLogger +}; diff --git a/packages/logging/test/fixtures/values.fixture.ts b/packages/logging/test/fixtures/values.fixture.ts new file mode 100644 index 00000000..a5f68154 --- /dev/null +++ b/packages/logging/test/fixtures/values.fixture.ts @@ -0,0 +1,38 @@ + +const errorWithStacktrace = new Error('error with stacktrace'); +errorWithStacktrace.stack = 'Stacktrace'; + +const errorWithoutStacktrace = new Error('error without stacktrace'); +errorWithoutStacktrace.stack = undefined; + +export const INPUT = +{ + STRING: 'value', + NUMBER: 1, + BOOLEAN: true, + OBJECT: { key: 'value' }, + ARRAY: ['value1', 'value2'], + FUNCTION: () => 'value', + UNDEFINED: undefined, + NULL: null, + NESTED_OBJECT: { key: { key: { key: 'value'} } }, + NESTED_ARRAY: ['value', ['value', ['value']]], + ERROR_WITH_STACKTRACE: errorWithStacktrace, + ERROR_WITHOUT_STACKTRACE: errorWithoutStacktrace +}; + +export const OUTPUT = +{ + STRING: '[INFO] value', + NUMBER: '[INFO] 1', + BOOLEAN: '[INFO] true', + OBJECT: '[INFO] {"key":"value"}', + ARRAY: '[INFO] [\n value1,\n value2\n]', + FUNCTION: '[INFO] function', + UNDEFINED: '[INFO] undefined', + NULL: '[INFO] null', + NESTED_OBJECT: '[INFO] {"key":{"key":{"key":"value"}}}', + NESTED_ARRAY: '[INFO] [\n value,\n [\n value,\n [\n value\n ]\n ]\n]', + ERROR_WITH_STACKTRACE: '[INFO] Stacktrace', + ERROR_WITHOUT_STACKTRACE: '[INFO] error without stacktrace' +}; diff --git a/packages/logging/test/fixtures/writer.fixture.ts b/packages/logging/test/fixtures/writer.fixture.ts new file mode 100644 index 00000000..9c90ec72 --- /dev/null +++ b/packages/logging/test/fixtures/writer.fixture.ts @@ -0,0 +1,54 @@ + +import type { Writer } from '../../src'; + +class MemoryWriter implements Writer +{ + #messages: string[] = []; + + get messages(): string[] + { + return this.#messages; + } + + get lastMessage(): string | undefined + { + return this.#messages[this.#messages.length - 1]; + } + + log(message: string): void + { + this.#messages.push(message); + } + + debug(message: string): void + { + this.#messages.push(message); + } + + info(message: string): void + { + this.#messages.push(message); + } + + warn(message: string): void + { + this.#messages.push(message); + } + + error(message: string): void + { + this.#messages.push(message); + } + + fatal(message: string): void + { + this.#messages.push(message); + } + + clear(): void + { + this.#messages = []; + } +} + +export const writer = new MemoryWriter(); diff --git a/packages/logging/tsconfig.json b/packages/logging/tsconfig.json new file mode 100644 index 00000000..e66fac12 --- /dev/null +++ b/packages/logging/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "rootDir": "./src/", + "moduleResolution": "node", + "declaration": true, + "outDir": "./dist", + "removeComments": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "skipLibCheck": true + }, + "exclude": [ + "vite.config.ts", + "node_modules", + "dist", + "test" + ] +} \ No newline at end of file diff --git a/packages/logging/vite.config.ts b/packages/logging/vite.config.ts new file mode 100644 index 00000000..f5182e1f --- /dev/null +++ b/packages/logging/vite.config.ts @@ -0,0 +1,10 @@ +// vite.config.ts +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + coverage: { + provider: 'v8' + }, + }, +}); diff --git a/packages/middleware/CHANGELOG.md b/packages/middleware/CHANGELOG.md new file mode 100644 index 00000000..80f29432 --- /dev/null +++ b/packages/middleware/CHANGELOG.md @@ -0,0 +1,4 @@ + +# Changelog + +This package doesn't keep a changelog. See the changelog in the [github repository](https://github.com/MaskingTechnology/jitar/blob/main/CHANGELOG.md) \ No newline at end of file diff --git a/packages/middleware/README.md b/packages/middleware/README.md new file mode 100644 index 00000000..d5b879b5 --- /dev/null +++ b/packages/middleware/README.md @@ -0,0 +1,9 @@ + +# Jitar Middleware + +This package provides middleware support for the [Jitar](https://jitar.dev) runtime. + +For more information about Jitar: + +* [Visit our website](https://jitar.dev) +* [Read the documentation](https://docs.jitar.dev). diff --git a/packages/middleware/package.json b/packages/middleware/package.json new file mode 100644 index 00000000..fa46616d --- /dev/null +++ b/packages/middleware/package.json @@ -0,0 +1,22 @@ +{ + "name": "@jitar/middleware", + "version": "0.7.4", + "description": "Middleware library for the Jitar runtime.", + "author": "Masking Technology (https://jitar.dev)", + "license": "MIT", + "type": "module", + "types": "dist/index.d.ts", + "exports": "./dist/index.js", + "private": true, + "scripts": { + "test": "vitest run", + "test-coverage": "vitest run --coverage", + "lint": "eslint . --ext .ts", + "build": "tsc -p tsconfig.json", + "clean": "rm -rf dist" + }, + "dependencies": { + "@jitar/errors": "*", + "@jitar/execution": "*" + } +} diff --git a/packages/middleware/src/MiddlewareManager.ts b/packages/middleware/src/MiddlewareManager.ts new file mode 100644 index 00000000..1b3d298e --- /dev/null +++ b/packages/middleware/src/MiddlewareManager.ts @@ -0,0 +1,54 @@ + +import { Request, Response, StatusCodes } from '@jitar/execution'; + +import InvalidMiddleware from './errors/InvalidMiddleware'; +import type Middleware from './interfaces/Middleware'; +import type NextHandler from './types/NextHandler'; + +export default class MiddlewareManager +{ + #middlewares: Middleware[] = []; + + addMiddleware(middleware: Middleware): void + { + if (middleware?.handle === undefined) + { + throw new InvalidMiddleware(); + } + + this.#middlewares.push(middleware); + } + + getMiddleware(type: Function): Middleware | undefined + { + return this.#middlewares.find(middleware => middleware instanceof type); + } + + clearMiddlewares(): void + { + this.#middlewares = []; + } + + handle(request: Request): Promise + { + // Middleware will be executed in the order they were added. + + const startHandler = this.#getNextHandler(request, 0); + + return startHandler(); + } + + #getNextHandler(request: Request, index: number): NextHandler + { + const next = this.#middlewares[index]; + + if (next === undefined) + { + return async () => new Response(StatusCodes.OK); + } + + const nextHandler = this.#getNextHandler(request, index + 1); + + return async () => { return next.handle(request, nextHandler); }; + } +} diff --git a/packages/middleware/src/errors/InvalidMiddleware.ts b/packages/middleware/src/errors/InvalidMiddleware.ts new file mode 100644 index 00000000..a0bde52e --- /dev/null +++ b/packages/middleware/src/errors/InvalidMiddleware.ts @@ -0,0 +1,10 @@ + +import { ServerError } from '@jitar/errors'; + +export default class InvalidMiddleware extends ServerError +{ + constructor() + { + super('Invalid middleware'); + } +} diff --git a/packages/middleware/src/index.ts b/packages/middleware/src/index.ts new file mode 100644 index 00000000..97dae19b --- /dev/null +++ b/packages/middleware/src/index.ts @@ -0,0 +1,6 @@ + +export { default as Middleware } from './interfaces/Middleware'; + +export { default as NextHandler } from './types/NextHandler'; + +export { default as MiddlewareManager } from './MiddlewareManager'; diff --git a/packages/middleware/src/interfaces/Middleware.ts b/packages/middleware/src/interfaces/Middleware.ts new file mode 100644 index 00000000..8d016695 --- /dev/null +++ b/packages/middleware/src/interfaces/Middleware.ts @@ -0,0 +1,11 @@ + +import type { Request, Response } from '@jitar/execution'; + +import type NextHandler from '../types/NextHandler'; + +interface Middleware +{ + handle(request: Request, next: NextHandler): Promise; +} + +export default Middleware; diff --git a/packages/runtime/src/types/NextHandler.ts b/packages/middleware/src/types/NextHandler.ts similarity index 60% rename from packages/runtime/src/types/NextHandler.ts rename to packages/middleware/src/types/NextHandler.ts index 3c07d95b..4341848e 100644 --- a/packages/runtime/src/types/NextHandler.ts +++ b/packages/middleware/src/types/NextHandler.ts @@ -1,5 +1,5 @@ -import Response from '../models/Response.js'; +import type { Response } from '@jitar/execution'; type NextHandler = () => Promise; diff --git a/packages/runtime/test/services/ProcedureRuntime.spec.ts b/packages/middleware/test/MiddlewareManager.spec.ts similarity index 67% rename from packages/runtime/test/services/ProcedureRuntime.spec.ts rename to packages/middleware/test/MiddlewareManager.spec.ts index 47afaf95..cd8e7582 100644 --- a/packages/runtime/test/services/ProcedureRuntime.spec.ts +++ b/packages/middleware/test/MiddlewareManager.spec.ts @@ -1,14 +1,13 @@ import { describe, expect, it } from 'vitest'; -import Request from '../../src/models/Request'; -import Version from '../../src/models/Version'; +import { Request, RunModes, Version } from '@jitar/execution'; -import { RUNTIMES } from '../_fixtures/services/ProcedureRuntime.fixture'; +import { MIDDLEWARE_MANAGERS } from './fixtures'; -const runtime = RUNTIMES.MIDDLEWARE; +const defaultManager = MIDDLEWARE_MANAGERS.DEFAULT; -describe('services/ProcedureRuntime', () => +describe('MiddlewareManager', () => { describe('.handle(fqn, version, args, headers', () => { @@ -17,8 +16,8 @@ describe('services/ProcedureRuntime', () => const args = new Map(); const headers = new Map(); - const request = new Request('test', new Version(1, 0, 0), args, headers); - const response = await runtime.handle(request); + const request = new Request('test', new Version(1, 0, 0), args, headers, RunModes.NORMAL); + const response = await defaultManager.handle(request); expect(response.result).toBe('123'); expect(headers.get('first')).toBe('yes'); diff --git a/packages/middleware/test/fixtures/index.ts b/packages/middleware/test/fixtures/index.ts new file mode 100644 index 00000000..74f1711f --- /dev/null +++ b/packages/middleware/test/fixtures/index.ts @@ -0,0 +1,3 @@ + +export * from './middlewares.fixture'; +export * from './middlewareManagers.fixture'; diff --git a/packages/middleware/test/fixtures/middlewareManagers.fixture.ts b/packages/middleware/test/fixtures/middlewareManagers.fixture.ts new file mode 100644 index 00000000..8a546c93 --- /dev/null +++ b/packages/middleware/test/fixtures/middlewareManagers.fixture.ts @@ -0,0 +1,14 @@ + +import MiddlewareManager from '../../src/MiddlewareManager'; + +import { MIDDLEWARES } from './middlewares.fixture'; + +const manager = new MiddlewareManager(); +manager.addMiddleware(MIDDLEWARES.FIRST); +manager.addMiddleware(MIDDLEWARES.SECOND); +manager.addMiddleware(MIDDLEWARES.THIRD); + +export const MIDDLEWARE_MANAGERS = +{ + DEFAULT: manager +}; diff --git a/packages/runtime/test/_fixtures/interfaces/Middleware.fixture.ts b/packages/middleware/test/fixtures/middlewares.fixture.ts similarity index 83% rename from packages/runtime/test/_fixtures/interfaces/Middleware.fixture.ts rename to packages/middleware/test/fixtures/middlewares.fixture.ts index 388812f4..9a7f59c0 100644 --- a/packages/runtime/test/_fixtures/interfaces/Middleware.fixture.ts +++ b/packages/middleware/test/fixtures/middlewares.fixture.ts @@ -1,7 +1,7 @@ -import Request from '../../../src/models/Request'; -import Response from '../../../src/models/Response'; -import Middleware from '../../../src/interfaces/Middleware'; +import { Request, Response, StatusCodes } from '@jitar/execution'; + +import Middleware from '../../src/interfaces/Middleware'; class FirstMiddleware implements Middleware { @@ -41,15 +41,13 @@ class ThirdMiddleware implements Middleware request.setHeader('third', 'yes'); request.setHeader('last', '3'); - return new Response('3'); + return new Response(StatusCodes.OK, '3'); } } -const MIDDLEWARES = +export const MIDDLEWARES = { FIRST: new FirstMiddleware(), SECOND: new SecondMiddleware(), THIRD: new ThirdMiddleware() }; - -export { MIDDLEWARES }; diff --git a/packages/middleware/tsconfig.json b/packages/middleware/tsconfig.json new file mode 100644 index 00000000..e66fac12 --- /dev/null +++ b/packages/middleware/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "rootDir": "./src/", + "moduleResolution": "node", + "declaration": true, + "outDir": "./dist", + "removeComments": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "skipLibCheck": true + }, + "exclude": [ + "vite.config.ts", + "node_modules", + "dist", + "test" + ] +} \ No newline at end of file diff --git a/packages/middleware/vite.config.ts b/packages/middleware/vite.config.ts new file mode 100644 index 00000000..f5182e1f --- /dev/null +++ b/packages/middleware/vite.config.ts @@ -0,0 +1,10 @@ +// vite.config.ts +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + coverage: { + provider: 'v8' + }, + }, +}); diff --git a/packages/plugin-vite/package.json b/packages/plugin-vite/package.json index c8f700ea..5f4500c2 100644 --- a/packages/plugin-vite/package.json +++ b/packages/plugin-vite/package.json @@ -26,9 +26,6 @@ "publishConfig": { "access": "public" }, - "dependencies": { - "@jitar/reflection": "*" - }, "peerDependencies": { "vite": ">=4.0.0 || >=5.0.0" }, diff --git a/packages/plugin-vite/src/index.ts b/packages/plugin-vite/src/index.ts index e514608a..08bc1c40 100644 --- a/packages/plugin-vite/src/index.ts +++ b/packages/plugin-vite/src/index.ts @@ -1,133 +1,259 @@ import path from 'path'; +import fs from 'fs'; + import { PluginOption, normalizePath, ResolvedConfig } from 'vite'; -import { Reflector, ReflectionFunction } from '@jitar/reflection'; -const reflector = new Reflector(); +const JITAR_SOURCE_ID = 'jitar'; +const JITAR_CLIENT_ID = 'jitar/client'; +const JITAR_BUNDLE_ID = 'jitar-bundle'; -function formatPath(path: string) +function formatDir(dir: string) { - path = normalizePath(path); + dir = normalizePath(dir); - if (path.startsWith('/')) + if (dir.startsWith('/')) { - path = path.substring(1); + dir = dir.substring(1); } - if (path.endsWith('/')) + if (dir.endsWith('/')) { - path = path.substring(0, path.length - 1); + dir = dir.substring(0, dir.length - 1); } - return path; + return dir; } -function createServerConfig(jitarUrl: string) +function assureExtension(filename: string) { - return { - build: - { - target: 'esnext' - }, - server: { - proxy: { - '/rpc': jitarUrl, - '/jitar': jitarUrl, - '/modules': jitarUrl, - } - } - }; + if (filename.endsWith('.js')) + { + return filename; + } + + return `${filename}.js`; } -function createBootstrapCode(segments: string[], middlewares: string[]): string +function makeShared(filename: string) { - const segmentString = segments.map(segment => `'${segment}'`).join(', '); - const middlewareString = middlewares.map(middleware => `'${middleware}'`).join(', '); - - return ``; + return assureExtension(filename).replace('.js', '.shared.js'); } -async function createImportCode(code: string, id: string, jitarFullPath: string, jitarPath: string): Promise +type PluginConfig = { - // Translate the id to a relative path with a .js extension - const relativeId = id - .replace(jitarFullPath, '') - .replace('.ts', '.js'); - - const module = reflector.parse(code); - const exported = module.exported; - - // Extract all exports from the module - const allKeys = [...exported.keys()]; - const functionKeys = allKeys.filter(key => key !== 'default' && exported.get(key) instanceof ReflectionFunction); - - let importCode = ''; - let exportCode = ''; - - if (exported.has('default')) - { - importCode += `const defaultExport = module.default;\n`; - exportCode += `export default defaultExport;\n`; - } - - if (functionKeys.length > 0) - { - importCode += functionKeys.map(key => `const ${key} = module.${key};`).join('\n'); - exportCode += `export { ${functionKeys.join(', ')} };\n`; - } - - return 'import { getClient } from "/jitar/client.js";\n' - + `const module = await (await getClient()).import("./${jitarPath}${relativeId}");\n` - + importCode - + exportCode; -} + sourceDir: string; + targetDir: string; + jitarDir: string; + jitarUrl: string; + segments?: string[]; + middleware?: string[]; +}; -export default function viteJitar(sourcePath: string, jitarPath: string, jitarUrl: string, segments: string[] = [], middlewares: string[] = []): PluginOption +export default function viteJitar(pluginConfig: PluginConfig): PluginOption { - sourcePath = formatPath(sourcePath); - jitarPath = formatPath(jitarPath); + const sourceDir = formatDir(pluginConfig.sourceDir); + const targetDir = formatDir(pluginConfig.targetDir); + const jitarDir = formatDir(pluginConfig.jitarDir); + const jitarUrl = pluginConfig.jitarUrl; + const segments = pluginConfig.segments ?? []; + const middlewares = pluginConfig.middleware ?? []; - let jitarFullPath: string | undefined = undefined; + const scopes = ['shared', ...segments, 'remote']; + + let rootPath: string | undefined; + let sourcePath: string | undefined; + let targetPath: string | undefined; + let outputPath: string | undefined; + let jitarPath: string | undefined; + let jitarBundleFilename: string | undefined; + let jitarBundleImported = false; return { name: 'jitar-plugin-vite', - enforce: 'post', // After Vite converted the code to JS - //apply: '...', // Apply in serve and build mode config() { - return createServerConfig(jitarUrl); + return { + build: { target: 'esnext' }, + server: { proxy: { '/rpc': jitarUrl }} + }; }, configResolved(resolvedConfig: ResolvedConfig) { - jitarFullPath = path.join(resolvedConfig.root, sourcePath, jitarPath); + rootPath = path.join(resolvedConfig.root); + sourcePath = path.join(rootPath, sourceDir); + targetPath = path.join(rootPath, targetDir); + outputPath = path.join(targetPath, resolvedConfig.build.assetsDir); + jitarPath = path.join(sourcePath, jitarDir); }, - resolveId(source: string) + options(options) { - if (source === '/jitar/client.js') + // Add the jitar client bundle to the input + + if (options.input === undefined) + { + options.input = JITAR_BUNDLE_ID; + } + else if (typeof options.input === 'string') + { + options.input = [options.input, JITAR_BUNDLE_ID]; + } + else if (Array.isArray(options.input)) { - return { id: source, external: 'absolute' }; + options.input.push(JITAR_BUNDLE_ID); + } + else if (typeof options.input === 'object') + { + options.input.additionalEntry = JITAR_BUNDLE_ID; } - return null; + return options; }, - async transform(code: string, id: string) + resolveId: { - if (jitarFullPath === undefined || id.includes(jitarFullPath) === false) + order: 'pre', + async handler(source: string, importer: string | undefined, options: object) { - return code; + if (source === JITAR_BUNDLE_ID) + { + return source; + } + + if (source === JITAR_SOURCE_ID) + { + // Redirect all jitar imports to the jitar client bundle + // so we can bundle the client code with the application + + if (importer?.endsWith('.segment.js') === false) + { + // Flag that the jitar bundle was imported by the application + // so we can avoid adding it to the HTML + + jitarBundleImported = true; + } + + return JITAR_BUNDLE_ID; + } + + const resolution = await this.resolve(source, importer, options); + + if (resolution === null || jitarPath === undefined || resolution.id.includes(jitarPath) === false) + { + return null; + } + + const cacheId = resolution.id.replace(`/${sourceDir}/`, `/${targetDir}/`); + + for (const scope of scopes) + { + const scopeId = cacheId.replace('.ts', `.${scope}.js`); + + if (fs.existsSync(scopeId)) + { + return scopeId; + } + } + + return resolution.id; } + }, + + load(id) + { + // Create the jitar client bundle content + + if (id !== JITAR_BUNDLE_ID) + { + return null; + } + + const segmentFiles = segments.map(name => `${targetPath}/${name}.segment.js`); + const middlewareFiles = middlewares.map(name => makeShared(`${targetPath}/${name}`)); + + const jitarImport = `import { ClientBuilder, HttpRemoteBuilder } from "${JITAR_CLIENT_ID}";`; + const segmentImports = segmentFiles.map((filename, index) => `import { default as $S${index} } from "${filename}";`).join(''); + const middlewareImports = middlewareFiles.map((filename, index) => `import { default as $M${index} } from "${filename}";`).join(''); + const imports = [jitarImport, segmentImports, middlewareImports].join('\n'); - return createImportCode(code, id, jitarFullPath, jitarPath); + const remoteUrl = 'const remoteUrl = document.location.origin;'; + const segmentsItems = segments.map((_, index) => `$S${index}`).join(', '); + const segmentsArray = `const segments = [${segmentsItems}];`; + const middlewareItems = middlewares.map((_, index) => `$M${index}`).join(', '); + const middlewareArray = `const middleware = [${middlewareItems}];`; + const declarations = [remoteUrl, segmentsArray, middlewareArray].join('\n'); + + const remoteBuilder = 'const remoteBuilder = new HttpRemoteBuilder();'; + const clientBuilder = 'const clientBuilder = new ClientBuilder(remoteBuilder);'; + const build = 'clientBuilder.build({remoteUrl, segments, middleware});'; + const client = [remoteBuilder, clientBuilder, build].join('\n'); + + const exports = `export * from "${JITAR_CLIENT_ID}";`; + + return [imports, declarations, client, exports].join('\n'); + }, + + generateBundle(options, bundle) + { + // Find the jitar client bundle so we can add it later to the HTML + + const bundles = Object.entries(bundle); + + for (const [fileName, chunk] of bundles) + { + if (chunk.type === 'chunk' && chunk.name === JITAR_BUNDLE_ID) + { + jitarBundleFilename = fileName; + + break; + } + } }, transformIndexHtml(html) { - return html.replace('', `${createBootstrapCode(segments, middlewares)}`); + // Add the jitar client bundle to the HTML if it wasn't imported + // by any of the application files. + + if (jitarBundleImported === true) + { + return html; + } + + if (jitarBundleFilename === undefined) + { + // Dev mode: insert the pre generated jitar bundle + + if (outputPath === undefined) + { + console.warn('Output path not found!'); + + return html; + } + + const filenames = fs.readdirSync(outputPath); + + const jitarFilename = filenames.find(fileName => fileName.startsWith(JITAR_BUNDLE_ID) && fileName.endsWith('.js')); + + if (jitarFilename === undefined) + { + console.warn('Jitar bundle not found! Did you build the application first?'); + + return html; + } + + const jitarBundle = fs.readFileSync(path.join(outputPath, jitarFilename), 'utf-8'); + + return html.replace('${jitarBundle}\n \n (https://jitar.dev)", - "license": "MIT", - "type": "module", - "types": "dist/lib.d.ts", - "exports": { - ".": "./dist/lib.js" - }, - "files": [ - "CHANGELOG.md", - "README.md", - "dist" - ], - "publishConfig": { - "access": "public" - }, - "scripts": { - "test": "vitest run", - "test-coverage": "vitest run --coverage", - "lint": "eslint . --ext .ts", - "build": "tsc -p tsconfig.json", - "clean": "rm -rf dist", - "prepublishOnly": "npm run clean && npm run build" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/MaskingTechnology/jitar.git" - }, - "bugs": { - "url": "https://github.com/MaskingTechnology/jitar/issues" - }, - "homepage": "https://jitar.dev", - "keywords": [ - "javascript", - "reflection", - "jitar" - ] -} diff --git a/packages/reflection/src/lib.ts b/packages/reflection/src/lib.ts deleted file mode 100644 index fa5f0c6c..00000000 --- a/packages/reflection/src/lib.ts +++ /dev/null @@ -1,24 +0,0 @@ - -// Models -export { default as ReflectionAlias } from './models/ReflectionAlias.js'; -export { default as ReflectionArray } from './models/ReflectionArray.js'; -export { default as ReflectionClass } from './models/ReflectionClass.js'; -export { default as ReflectionDeclaration } from './models/ReflectionDeclaration.js'; -export { default as ReflectionDestructuredArray } from './models/ReflectionDestructuredArray.js'; -export { default as ReflectionDestructuredObject } from './models/ReflectionDestructuredObject.js'; -export { default as ReflectionDestructuredValue } from './models/ReflectionDestructuredValue.js'; -export { default as ReflectionExport } from './models/ReflectionExport.js'; -export { default as ReflectionExpression } from './models/ReflectionExpression.js'; -export { default as ReflectionField } from './models/ReflectionField.js'; -export { default as ReflectionFunction } from './models/ReflectionFunction.js'; -export { default as ReflectionGenerator } from './models/ReflectionGenerator.js'; -export { default as ReflectionGetter } from './models/ReflectionGetter.js'; -export { default as ReflectionImport } from './models/ReflectionImport.js'; -export { default as ReflectionMember } from './models/ReflectionMember.js'; -export { default as ReflectionModule } from './models/ReflectionModule.js'; -export { default as ReflectionObject } from './models/ReflectionObject.js'; -export { default as ReflectionParameter } from './models/ReflectionParameter.js'; -export { default as ReflectionSetter } from './models/ReflectionSetter.js'; - -// Root -export { default as Reflector } from './Reflector.js'; diff --git a/packages/reflection/src/models/ReflectionArray.ts b/packages/reflection/src/models/ReflectionArray.ts deleted file mode 100644 index cf0cb80c..00000000 --- a/packages/reflection/src/models/ReflectionArray.ts +++ /dev/null @@ -1,7 +0,0 @@ - -import ReflectionValue from './ReflectionValue.js'; - -export default class ReflectionArray extends ReflectionValue -{ - -} diff --git a/packages/reflection/src/models/ReflectionDeclaration.ts b/packages/reflection/src/models/ReflectionDeclaration.ts deleted file mode 100644 index b65b8d05..00000000 --- a/packages/reflection/src/models/ReflectionDeclaration.ts +++ /dev/null @@ -1,27 +0,0 @@ - -import ReflectionIdentifier from './ReflectionIdentifier.js'; -import ReflectionMember from './ReflectionMember.js'; -import ReflectionValue from './ReflectionValue.js'; - -export default class ReflectionDeclaration extends ReflectionMember -{ - #identifier: ReflectionIdentifier; - #value: ReflectionValue | undefined; - - constructor(identifier: ReflectionIdentifier, value: ReflectionValue | undefined, isStatic = false, isPrivate = false) - { - super(identifier.toString(), isStatic, isPrivate); - - this.#identifier = identifier; - this.#value = value; - } - - get identifier() { return this.#identifier; } - - get value() { return this.#value; } - - toString(): string - { - return `${this.name}${this.value ? ' = ' + this.value.toString() : ''}`; - } -} diff --git a/packages/reflection/src/models/ReflectionDestructuredArray.ts b/packages/reflection/src/models/ReflectionDestructuredArray.ts deleted file mode 100644 index f4f3d064..00000000 --- a/packages/reflection/src/models/ReflectionDestructuredArray.ts +++ /dev/null @@ -1,10 +0,0 @@ - -import ReflectionDestructuredValue from './ReflectionDestructuredValue.js'; - -export default class ReflectionDestructuredArray extends ReflectionDestructuredValue -{ - toString(): string - { - return `[ ${super.toString()} ]`; - } -} diff --git a/packages/reflection/src/models/ReflectionDestructuredObject.ts b/packages/reflection/src/models/ReflectionDestructuredObject.ts deleted file mode 100644 index 6fdf7a0a..00000000 --- a/packages/reflection/src/models/ReflectionDestructuredObject.ts +++ /dev/null @@ -1,10 +0,0 @@ - -import ReflectionDestructuredValue from './ReflectionDestructuredValue.js'; - -export default class ReflectionDestructuredObject extends ReflectionDestructuredValue -{ - toString(): string - { - return `{ ${super.toString()} }`; - } -} diff --git a/packages/reflection/src/models/ReflectionExpression.ts b/packages/reflection/src/models/ReflectionExpression.ts deleted file mode 100644 index c18f5bcf..00000000 --- a/packages/reflection/src/models/ReflectionExpression.ts +++ /dev/null @@ -1,7 +0,0 @@ - -import ReflectionValue from './ReflectionValue.js'; - -export default class ReflectionExpression extends ReflectionValue -{ - -} diff --git a/packages/reflection/src/models/ReflectionGetter.ts b/packages/reflection/src/models/ReflectionGetter.ts deleted file mode 100644 index 052e51fa..00000000 --- a/packages/reflection/src/models/ReflectionGetter.ts +++ /dev/null @@ -1,10 +0,0 @@ - -import ReflectionFunction from './ReflectionFunction.js'; - -export default class ReflectionGetter extends ReflectionFunction -{ - toString(): string - { - return `get ${super.toString()}`; - } -} diff --git a/packages/reflection/src/models/ReflectionIdentifier.ts b/packages/reflection/src/models/ReflectionIdentifier.ts deleted file mode 100644 index 944ad16e..00000000 --- a/packages/reflection/src/models/ReflectionIdentifier.ts +++ /dev/null @@ -1,7 +0,0 @@ - -import ReflectionDestructuredArray from './ReflectionDestructuredArray.js'; -import ReflectionDestructuredObject from './ReflectionDestructuredObject.js'; - -type ReflectionIdentifier = string | ReflectionDestructuredArray | ReflectionDestructuredObject - -export default ReflectionIdentifier; diff --git a/packages/reflection/src/models/ReflectionModule.ts b/packages/reflection/src/models/ReflectionModule.ts deleted file mode 100644 index 15a2f27f..00000000 --- a/packages/reflection/src/models/ReflectionModule.ts +++ /dev/null @@ -1,152 +0,0 @@ - -import ReflectionClass from './ReflectionClass.js'; -import ReflectionDeclaration from './ReflectionDeclaration.js'; -import ReflectionExport from './ReflectionExport.js'; -import ReflectionFunction from './ReflectionFunction.js'; -import ReflectionGenerator from './ReflectionGenerator.js'; -import ReflectionImport from './ReflectionImport.js'; -import ReflectionMember from './ReflectionMember.js'; -import ReflectionScope from './ReflectionScope.js'; - -export default class ReflectionModule -{ - #scope: ReflectionScope; - - constructor(scope: ReflectionScope) - { - this.#scope = scope; - } - - get scope(): ReflectionScope { return this.#scope; } - - get members(): ReflectionMember[] { return this.#scope.members; } - - get exportedMembers(): ReflectionMember[] { return this.#filterExported(this.#scope.members); } - - get imports(): ReflectionImport[] { return this.#scope.imports; } - - get exports(): ReflectionExport[] { return this.#scope.exports; } - - get declarations(): ReflectionDeclaration[] { return this.#scope.declarations; } - - get exportedDeclarations(): ReflectionDeclaration[] { return this.#filterExported(this.#scope.declarations) as ReflectionDeclaration[]; } - - get functions(): ReflectionFunction[] { return this.#scope.functions; } - - get exportedFunctions(): ReflectionFunction[] { return this.#filterExported(this.#scope.functions) as ReflectionFunction[]; } - - get generators(): ReflectionFunction[] { return this.#scope.generators; } - - get exportedGenerators(): ReflectionGenerator[] { return this.#filterExported(this.#scope.generators) as ReflectionGenerator[]; } - - get classes(): ReflectionClass[] { return this.#scope.classes; } - - get exportedClasses(): ReflectionClass[] { return this.#filterExported(this.#scope.classes) as ReflectionClass[]; } - - get exported(): Map - { - const exported = new Map(); - - for (const exportItem of this.exports) - { - for (const alias of exportItem.members) - { - const member = this.getMember(alias.name); - - if (member !== undefined) - { - exported.set(alias.as, member); - } - } - } - - return exported; - } - - getMember(name: string): ReflectionMember | undefined - { - return this.#scope.getMember(name); - } - - getDeclaration(name: string): ReflectionDeclaration | undefined - { - return this.#scope.getDeclaration(name); - } - - getFunction(name: string): ReflectionFunction | undefined - { - return this.#scope.getFunction(name); - } - - getGenerator(name: string): ReflectionFunction | undefined - { - return this.#scope.getGenerator(name); - } - - getClass(name: string): ReflectionClass | undefined - { - return this.#scope.getClass(name); - } - - hasMember(name: string): boolean - { - return this.#scope.hasMember(name); - } - - hasDeclaration(name: string): boolean - { - return this.#scope.hasDeclaration(name); - } - - hasFunction(name: string): boolean - { - return this.#scope.hasFunction(name); - } - - hasGenerator(name: string): boolean - { - return this.#scope.hasGenerator(name); - } - - hasClass(name: string): boolean - { - return this.#scope.hasClass(name); - } - - isExported(member: ReflectionMember): boolean - { - for (const exportItem of this.exports) - { - for (const alias of exportItem.members) - { - if (alias.name === member.name) - { - return true; - } - } - } - - return false; - } - - getExported(name: string): ReflectionMember | undefined - { - for (const exportItem of this.exports) - { - for (const alias of exportItem.members) - { - if (alias.as === name) - { - return this.getMember(alias.name); - } - } - } - - return undefined; - } - - #filterExported(members: ReflectionMember[]): ReflectionMember[] - { - return members.filter(member => this.isExported(member)); - } -} diff --git a/packages/reflection/src/models/ReflectionObject.ts b/packages/reflection/src/models/ReflectionObject.ts deleted file mode 100644 index 8243185e..00000000 --- a/packages/reflection/src/models/ReflectionObject.ts +++ /dev/null @@ -1,7 +0,0 @@ - -import ReflectionValue from './ReflectionValue.js'; - -export default class ReflectionObject extends ReflectionValue -{ - -} diff --git a/packages/reflection/src/models/ReflectionParameter.ts b/packages/reflection/src/models/ReflectionParameter.ts deleted file mode 100644 index b6b055cd..00000000 --- a/packages/reflection/src/models/ReflectionParameter.ts +++ /dev/null @@ -1,8 +0,0 @@ - -import ReflectionDestructuredArray from './ReflectionDestructuredArray.js'; -import ReflectionField from './ReflectionField.js'; -import ReflectionDestructuredObject from './ReflectionDestructuredObject.js'; - -type ReflectionParameter = ReflectionField | ReflectionDestructuredObject | ReflectionDestructuredArray; - -export default ReflectionParameter; diff --git a/packages/reflection/src/models/ReflectionScope.ts b/packages/reflection/src/models/ReflectionScope.ts deleted file mode 100644 index 2d42e165..00000000 --- a/packages/reflection/src/models/ReflectionScope.ts +++ /dev/null @@ -1,126 +0,0 @@ - -import ReflectionMember from './ReflectionMember.js'; -import ReflectionGetter from './ReflectionGetter.js'; -import ReflectionSetter from './ReflectionSetter.js'; -import ReflectionFunction from './ReflectionFunction.js'; -import ReflectionClass from './ReflectionClass.js'; -import ReflectionImport from './ReflectionImport.js'; -import ReflectionExport from './ReflectionExport.js'; -import ReflectionGenerator from './ReflectionGenerator.js'; -import ReflectionDeclaration from './ReflectionDeclaration.js'; - -// Required to work after minification. -const IMPORT_NAME = ReflectionImport.name; -const EXPORT_NAME = ReflectionExport.name; -const DECLARATION_NAME = ReflectionDeclaration.name; -const FUNCTION_NAME = ReflectionFunction.name; -const GETTER_NAME = ReflectionGetter.name; -const SETTER_NAME = ReflectionSetter.name; -const GENERATOR_NAME = ReflectionGenerator.name; -const CLASS_NAME = ReflectionClass.name; - -export default class ReflectionScope -{ - #members: ReflectionMember[]; - - constructor(members: ReflectionMember[]) - { - this.#members = members; - } - - // The constructor name is used to determine the type of the member. - // This makes sure that the member is of the exact type and not a subclass. - - get members(): ReflectionMember[] { return this.#members; } - - get imports(): ReflectionImport[] { return this.#members.filter(member => member.constructor.name === IMPORT_NAME) as ReflectionImport[]; } - - get exports(): ReflectionExport[] { return this.#members.filter(member => member.constructor.name === EXPORT_NAME) as ReflectionExport[]; } - - get declarations(): ReflectionDeclaration[] { return this.#members.filter(member => member.constructor.name === DECLARATION_NAME) as ReflectionDeclaration[]; } - - get functions(): ReflectionFunction[] { return this.#members.filter(member => member.constructor.name === FUNCTION_NAME) as ReflectionFunction[]; } - - get getters(): ReflectionGetter[] { return this.#members.filter(member => member.constructor.name === GETTER_NAME) as ReflectionGetter[]; } - - get setters(): ReflectionSetter[] { return this.#members.filter(member => member.constructor.name === SETTER_NAME) as ReflectionSetter[]; } - - get generators(): ReflectionGenerator[] { return this.#members.filter(member => member.constructor.name === GENERATOR_NAME) as ReflectionGenerator[]; } - - get classes(): ReflectionClass[] { return this.#members.filter(member => member.constructor.name === CLASS_NAME) as ReflectionClass[]; } - - getMember(name: string): ReflectionMember | undefined - { - return this.#members.find(member => member.name === name); - } - - getDeclaration(name: string): ReflectionDeclaration | undefined - { - return this.declarations.find(member => member.name === name); - } - - getFunction(name: string): ReflectionFunction | undefined - { - return this.functions.find(member => member.name === name); - } - - getGetter(name: string): ReflectionGetter | undefined - { - return this.getters.find(member => member.name === name); - } - - getSetter(name: string): ReflectionSetter | undefined - { - return this.setters.find(member => member.name === name); - } - - getGenerator(name: string): ReflectionGenerator | undefined - { - return this.generators.find(member => member.name === name); - } - - getClass(name: string): ReflectionClass | undefined - { - return this.classes.find(member => member.name === name); - } - - hasMember(name: string): boolean - { - return this.getMember(name) !== undefined; - } - - hasDeclaration(name: string): boolean - { - return this.getDeclaration(name) !== undefined; - } - - hasFunction(name: string): boolean - { - return this.getFunction(name) !== undefined; - } - - hasGetter(name: string): boolean - { - return this.getGetter(name) !== undefined; - } - - hasSetter(name: string): boolean - { - return this.getSetter(name) !== undefined; - } - - hasGenerator(name: string): boolean - { - return this.getGenerator(name) !== undefined; - } - - hasClass(name: string): boolean - { - return this.getClass(name) !== undefined; - } - - toString(): string - { - return this.#members.map(member => member.toString()).join('\n'); - } -} diff --git a/packages/reflection/src/models/ReflectionSetter.ts b/packages/reflection/src/models/ReflectionSetter.ts deleted file mode 100644 index 37f78409..00000000 --- a/packages/reflection/src/models/ReflectionSetter.ts +++ /dev/null @@ -1,10 +0,0 @@ - -import ReflectionFunction from './ReflectionFunction.js'; - -export default class ReflectionSetter extends ReflectionFunction -{ - toString(): string - { - return `set ${super.toString()}`; - } -} diff --git a/packages/reflection/src/parser/TokenList.ts b/packages/reflection/src/parser/TokenList.ts deleted file mode 100644 index 64a0f5b3..00000000 --- a/packages/reflection/src/parser/TokenList.ts +++ /dev/null @@ -1,8 +0,0 @@ - -import ItemList from './ItemList.js'; -import Token from './Token.js'; - -export default class TokenList extends ItemList -{ - -} diff --git a/packages/reflection/test/_fixtures/models/ReflectionClass.fixture.ts b/packages/reflection/test/_fixtures/models/ReflectionClass.fixture.ts deleted file mode 100644 index 465f74f2..00000000 --- a/packages/reflection/test/_fixtures/models/ReflectionClass.fixture.ts +++ /dev/null @@ -1,28 +0,0 @@ - -import ReflectionScope from '../../../src/models/ReflectionScope'; -import ReflectionField from '../../../src/models/ReflectionField'; -import ReflectionExpression from '../../../src/models/ReflectionExpression'; -import ReflectionFunction from '../../../src/models/ReflectionFunction'; -import ReflectionClass from '../../../src/models/ReflectionClass'; -import ReflectionGetter from '../../../src/models/ReflectionGetter'; -import ReflectionSetter from '../../../src/models/ReflectionSetter'; -import ReflectionDeclaration from '../../../src/models/ReflectionDeclaration'; - -const members = -[ - new ReflectionDeclaration('name', new ReflectionExpression('"Peter"'), false, true), - new ReflectionDeclaration('age', undefined, false, true), - new ReflectionDeclaration('length', undefined, false, false), - new ReflectionDeclaration('secret', undefined, false, true), - new ReflectionFunction('constructor', [new ReflectionField('age', undefined)], 'this.#age = age;'), - new ReflectionGetter('name', [], 'return this.#name;'), - new ReflectionGetter('age', [], 'return this.#age;'), - new ReflectionSetter('age', [new ReflectionField('age', undefined)], 'this.#age = age;'), - new ReflectionFunction('secretStuff', [], '', false, false, true), - new ReflectionFunction('toString', [], 'return `${this.#name} (${this.#age})`'), -]; - -const scope = new ReflectionScope(members); -const reflectionClass = new ReflectionClass('Person', undefined, scope); - -export { reflectionClass }; diff --git a/packages/reflection/test/_fixtures/models/ReflectionModule.fixture.ts b/packages/reflection/test/_fixtures/models/ReflectionModule.fixture.ts deleted file mode 100644 index de4515ca..00000000 --- a/packages/reflection/test/_fixtures/models/ReflectionModule.fixture.ts +++ /dev/null @@ -1,36 +0,0 @@ - -import ReflectionScope from '../../../src/models/ReflectionScope'; -import ReflectionModule from '../../../src/models/ReflectionModule'; -import ReflectionImport from '../../../src/models/ReflectionImport'; -import ReflectionAlias from '../../../src/models/ReflectionAlias'; -import ReflectionField from '../../../src/models/ReflectionField'; -import ReflectionExpression from '../../../src/models/ReflectionExpression'; -import ReflectionFunction from '../../../src/models/ReflectionFunction'; -import ReflectionClass from '../../../src/models/ReflectionClass'; -import ReflectionExport from '../../../src/models/ReflectionExport'; -import ReflectionGenerator from '../../../src/models/ReflectionGenerator'; -import ReflectionDeclaration from '../../../src/models/ReflectionDeclaration'; - -const members = -[ - new ReflectionImport([new ReflectionAlias('default', 'Person')], './Person.js'), - new ReflectionDeclaration('peter', new ReflectionExpression('new Person("Peter")')), - new ReflectionDeclaration('bas', new ReflectionExpression('new Person("Bas")')), - new ReflectionFunction('createJohn', [], 'return new Person("John")'), - new ReflectionFunction('sum', [new ReflectionField('a', undefined), new ReflectionField('b', undefined)], 'return a + b'), - new ReflectionGenerator('generateNumbers', [new ReflectionField('count', undefined)], 'for (let i = 0; i < count; i++) yield i;'), - new ReflectionGenerator('createJohn', [], 'yield new Person("John")'), - new ReflectionClass('Customer', 'Person', new ReflectionScope([])), - new ReflectionClass('Order', undefined, new ReflectionScope([])), - new ReflectionExport([ - new ReflectionAlias('sum', 'default'), - new ReflectionAlias('peter', 'peter'), - new ReflectionAlias('Customer', 'Customer'), - new ReflectionAlias('generateNumbers', 'generateNumbers') - ], undefined) -]; - -const scope = new ReflectionScope(members); -const reflectionModule = new ReflectionModule(scope); - -export { reflectionModule }; diff --git a/packages/reflection/test/_fixtures/models/ReflectionScope.fixture.ts b/packages/reflection/test/_fixtures/models/ReflectionScope.fixture.ts deleted file mode 100644 index 5394a076..00000000 --- a/packages/reflection/test/_fixtures/models/ReflectionScope.fixture.ts +++ /dev/null @@ -1,32 +0,0 @@ - -import ReflectionScope from '../../../src/models/ReflectionScope'; -import ReflectionField from '../../../src/models/ReflectionField'; -import ReflectionExpression from '../../../src/models/ReflectionExpression'; -import ReflectionFunction from '../../../src/models/ReflectionFunction'; -import ReflectionClass from '../../../src/models/ReflectionClass'; -import ReflectionGetter from '../../../src/models/ReflectionGetter'; -import ReflectionSetter from '../../../src/models/ReflectionSetter'; -import ReflectionImport from '../../../src/models/ReflectionImport'; -import ReflectionAlias from '../../../src/models/ReflectionAlias'; -import ReflectionExport from '../../../src/models/ReflectionExport'; -import ReflectionGenerator from '../../../src/models/ReflectionGenerator'; -import ReflectionDeclaration from '../../../src/models/ReflectionDeclaration'; - -const members = -[ - new ReflectionImport([new ReflectionAlias('default', 'Person')], './Person.js'), - new ReflectionDeclaration('name', new ReflectionExpression('"john"'), false, true), - new ReflectionDeclaration('age', new ReflectionExpression('42'), false, true), - new ReflectionFunction('createJohn', [], '{ return new Person("John") }'), - new ReflectionFunction('sum', [new ReflectionField('a', undefined), new ReflectionField('b', undefined)], 'return a + b'), - new ReflectionGetter('name', [], 'return this.#name;'), - new ReflectionGetter('age', [], 'return this.#age;'), - new ReflectionSetter('age', [new ReflectionField('age', undefined)], 'this.#age = age;'), - new ReflectionClass('Customer', 'Person', new ReflectionScope([])), - new ReflectionGenerator('createJohn', [], 'yield new Person("John")'), - new ReflectionExport([new ReflectionAlias('Customer', 'Customer'), new ReflectionAlias('sum', 'default')], undefined) -]; - -const reflectionScope = new ReflectionScope(members); - -export { reflectionScope }; diff --git a/packages/reflection/test/_fixtures/parser/ItemList.fixture.ts b/packages/reflection/test/_fixtures/parser/ItemList.fixture.ts deleted file mode 100644 index 2ac69dd8..00000000 --- a/packages/reflection/test/_fixtures/parser/ItemList.fixture.ts +++ /dev/null @@ -1,23 +0,0 @@ - -import ItemList from '../../../src/parser/ItemList'; - -const items: string[] = -[ - "a", - "b", - "c", - "d", - "e", - "f", - "g", - "h", - "i", - "j" -]; - -function createList(): ItemList -{ - return new ItemList(items); -} - -export { createList }; diff --git a/packages/reflection/test/_fixtures/parser/Parser.fixture.ts b/packages/reflection/test/_fixtures/parser/Parser.fixture.ts deleted file mode 100644 index 86a928ed..00000000 --- a/packages/reflection/test/_fixtures/parser/Parser.fixture.ts +++ /dev/null @@ -1,204 +0,0 @@ - -const VALUES = -{ - ARRAY: '[1, "foo", false, new Person("Peter", 42), { a: 1, b: 2 }]', - OBJECT: '{ key1: "value1", "key2": new Person().toString() }', - EXPRESSION:'new Number(Math.ceil(Math.random()) + 10).toString();', - EXPRESSION_GROUP: '(a + b) * c', - IF_ELSE: 'if (true) { return "value1"; } else { return "value2"; }', - TRY_CATCH_FINALLY: 'try { sum(1, 2); } catch (error) { console.error(error); } finally { console.log("finally"); }' -}; - -const IMPORTS = -{ - LOAD: "import 'module'", - IMPORT: "import { member } from 'module'", - IMPORT_AS: "import { member as alias } from 'module'", - IMPORT_ALL: "import * as name from 'module'", - IMPORT_DEFAULT: "import name from 'module'", - IMPORT_DEFAULT_MEMBER: "import name, { member } from 'module'", - IMPORT_DEFAULT_MEMBER_AS: "import name, { member as alias } from 'module'", - IMPORT_MULTIPLE_MEMBERS_AS: "import { name, member as alias } from 'module'", - IMPORT_DYNAMIC: "import('module')", -}; - -const EXPORTS = -{ - EXPORT: "export { member }", - EXPORT_AS: "export { member as alias }", - EXPORT_DEFAULT: "export default name", - EXPORT_MULTIPLE: "export { name, member }", - EXPORT_MULTIPLE_AS: "export { name, member as alias }", - EXPORT_CLASS_DECLARATION: "export class name {}", - EXPORT_FIELD_DECLARATION: "export const name = 'value'", - EXPORT_FUNCTION_DECLARATION: "export function name() {}", - EXPORT_ASYNC_FUNCTION_DECLARATION: "export async function name() {}", - REEXPORT_ALL: "export * from 'module'", - REEXPORT_MEMBER: "export { member } from 'module'", -}; - -const DECLARATIONS = -{ - EMPTY: "let name;", - CONST: "const name = 'const';", - LET: "let name = 'let';", - VAR: "var name = 'var';", - MULTIPLE: "let name1 = (1 + 2) * 3, name2, name3 = 'foo';", - EXPRESSION: `const number = new Number(Math.ceil(Math.random()) + 10).toString();`, - ARRAY: "const array = [ 'value1', 'value2' ];", - OBJECT: "const object = { key1: 'value1', key2: 'value2' };", - REGEX: "const regex = /regex/g;", - DESTRUCTURING_ARRAY: "const [value1, value2 = true] = array;", - DESTRUCTURING_OBJECT: "const {key1, key2 = false} = object;", - KEYWORD_AS_NAME: "const as = 'value';", - KEYWORD_AS_VALUE: "const alias = as;" -}; - -const FUNCTIONS = -{ - DECLARATION: "function name() {}", - ASYNC_DECLARATION: "async function name() {}", - EXPRESSION: "const name = function() {}", - ASYNC_EXPRESSION: "const name = async function() {}", - ARROW: "const name = () => {}", - ARROW_EXPRESSION: "const name = () => 'value';", - ARROW_ARGUMENT: 'const name = arg => arg;', - ASYNC_ARROW: "const name = async () => {}", - GENERATOR: "function* name() {}", - ASYNC_GENERATOR: "async function* name() {}", - EXPRESSION_GENERATOR: "const name = function*() {}", - ASYNC_EXPRESSION_GENERATOR: "const name = async function*() {}", - PARAMETERS: "function name(param1, param2) {}", - DEFAULT_PARAMETERS: "function name(param1 = 'value1', param2 = true) {}", - REST_PARAMETERS: "function name(...param1) {}", - DESTRUCTURING_PARAMETERS: "function name({ param1, param2 }, [ param3, param4 ]) {}", - DESTRUCTURING_DEFAULT_PARAMETERS: "function name({ param1 = 'value1', param2 = true }, [ param3 = 'value3', param4 = true ]) {}", - DESTRUCTURING_REST_PARAMETERS: "function name({ param1, param2 }, [ param3, ...param4 ]) {}", - SIMPLE_BODY: "function name() { return 'value'; }", - BLOCK_BODY: "function name() { if (true) { return 'value'; } }", - KEYWORD_AS_NAME: "function as() {}" -}; - -const CLASSES = -{ - DECLARATION: "class Name {}", - EXTENDS: "class Name extends Parent {}", - EXPRESSION: "const name = class {}", - MEMBERS: `class Name -{ - #field1 = 'value1'; - field2; - - static #field3 = "value3"; - static field4; - - constructor(field1, ...field2) - { - this.#field1 = field1; - this.field2 = field2; - } - - get #getter1() { return field1; } - - get getter2() { return field2; } - - static get #getter3() { return field3; } - - static get getter4() { return field4; } - - set #setter1(value) { this.#field1 = value; } - - set setter2(value) { this.#field2 = value; } - - static set #setter3(value) { this.#field3 = value; } - - static set setter4(value) { this.#field4 = value; } - - method1() { return this.#field1; } - - async method2() { return this.#field1; } - - static method3() { return this.#field1; } - - static async method4() { return this.#field1; } - - #method5() { return this.#field1; } - - *generator1() { yield 1; } - - async *generator2() { yield 1; } - - static async *generator3() { yield 1; } -}` -}; - -const MODULES = -{ - TERMINATED: -` -import { member } from 'module'; -import { member as alias } from 'module2'; - -const name = 'Peter' + ' van ' + 'Vliet'; - -export default function sum(a, b) { return a + b; } - -[1, 2, 3, 4, 5].sort((a, b) => a - b); - -try { sum(1, 2); } catch (error) { console.error(error); } - -export class Person -{ - #name; - #age; - - constructor(name, age) - { - this.#name = name; - this.#age = age; - } -} - -const peter = new Person(name, 42); - -async function async() { } -const a = async; -const b = async () => {}; - -const as = 12; -export { as as hi }; - -export { name, peter }; -`, - UNTERMINATED: -` - import { member } from 'module' - import { member as alias } from 'module2' - - const name = 'Peter' + ' van ' + 'Vliet' - - export default function sum(a, b) { return a + b } - - [1, 2, 3, 4, 5].sort((a, b) => a - b) - - try { sum(1, 2) } catch (error) { console.error(error) } - - export class Person - { - #name - #age - - constructor(name, age) - { - this.#name = name - this.#age = age - } - } - - const peter = new Person(name, 42) - - export { name, peter } -` -}; - -export { VALUES, IMPORTS, EXPORTS, DECLARATIONS, FUNCTIONS, CLASSES, MODULES }; diff --git a/packages/reflection/test/models/ReflectionModule.spec.ts b/packages/reflection/test/models/ReflectionModule.spec.ts deleted file mode 100644 index de07ab85..00000000 --- a/packages/reflection/test/models/ReflectionModule.spec.ts +++ /dev/null @@ -1,121 +0,0 @@ - -import { describe, expect, it } from 'vitest'; - -import ReflectionClass from '../../src/models/ReflectionClass'; -import ReflectionDeclaration from '../../src/models/ReflectionDeclaration'; -import ReflectionFunction from '../../src/models/ReflectionFunction'; -import ReflectionGenerator from '../../src/models/ReflectionGenerator'; -import ReflectionMember from '../../src/models/ReflectionMember'; - -import { reflectionModule } from '../_fixtures/models/ReflectionModule.fixture'; - -describe('models/ReflectionModule', () => -{ - // Scope tests are omitted - - describe('.exportedDeclarations', () => - { - it('should filter exported declarations', () => - { - const declarations = reflectionModule.exportedDeclarations; - expect(declarations.length).toBe(1); - - expect(declarations[0]).toBeInstanceOf(ReflectionDeclaration); - expect(declarations[0].name).toBe('peter'); - }); - }); - - describe('.exportedFunctions', () => - { - it('should filter exported functions', () => - { - const functions = reflectionModule.exportedFunctions; - expect(functions.length).toBe(1); - - expect(functions[0]).toBeInstanceOf(ReflectionFunction); - expect(functions[0].name).toBe('sum'); - }); - }); - - describe('.exportedGenerators', () => - { - it('should filter exported generators', () => - { - const generators = reflectionModule.exportedGenerators; - expect(generators.length).toBe(1); - - expect(generators[0]).toBeInstanceOf(ReflectionGenerator); - expect(generators[0].name).toBe('generateNumbers'); - }); - }); - - describe('.exportedClasses', () => - { - it('should filter exported classes', () => - { - const classes = reflectionModule.exportedClasses; - expect(classes.length).toBe(1); - - expect(classes[0]).toBeInstanceOf(ReflectionClass); - expect(classes[0].name).toBe('Customer'); - }); - }); - - describe('.exported', () => - { - it('should return a map with exported members only', () => - { - const exported = reflectionModule.exported; - expect(exported.size).toBe(4); - - const first = exported.get('default') as ReflectionFunction; - expect(first).toBeInstanceOf(ReflectionFunction); - expect(first.name).toBe('sum'); - - const second = exported.get('peter') as ReflectionDeclaration; - expect(second).toBeInstanceOf(ReflectionDeclaration); - expect(second.name).toBe('peter'); - - const third = exported.get('Customer') as ReflectionClass; - expect(third).toBeInstanceOf(ReflectionClass); - expect(third.name).toBe('Customer'); - - const fourth = exported.get('generateNumbers') as ReflectionGenerator; - expect(fourth).toBeInstanceOf(ReflectionGenerator); - expect(fourth.name).toBe('generateNumbers'); - }); - }); - - describe('.isExported(member)', () => - { - it('should indicate that a member is exported', () => - { - const member = reflectionModule.getMember('sum') as ReflectionMember; - const result = reflectionModule.isExported(member); - expect(result).toBe(true); - }); - - it('should indicate that a member is not exported', () => - { - const member = reflectionModule.getMember('createJohn') as ReflectionMember; - const result = reflectionModule.isExported(member); - expect(result).toBe(false); - }); - }); - - describe('.getExported(name)', () => - { - it('should get an exported member', () => - { - const member = reflectionModule.getExported('default') as ReflectionFunction; - expect(member).toBeInstanceOf(ReflectionFunction); - expect(member.name).toBe('sum'); - }); - - it('should not get a non-exported member', () => - { - const member = reflectionModule.getExported('createJohn'); - expect(member).toBe(undefined); - }); - }); -}); diff --git a/packages/reflection/test/parser/ItemList.spec.ts b/packages/reflection/test/parser/ItemList.spec.ts deleted file mode 100644 index d3462472..00000000 --- a/packages/reflection/test/parser/ItemList.spec.ts +++ /dev/null @@ -1,140 +0,0 @@ - -import { describe, expect, it } from 'vitest'; - -import { createList } from '../_fixtures/parser/ItemList.fixture'; - -describe('parser/ItemList', () => -{ - describe('.current', () => - { - it('should start at the first item', () => - { - const itemList = createList(); - - expect(itemList.current).toBe('a'); - }); - - it('should return the second item after a step', () => - { - const itemList = createList(); - itemList.step(); - - expect(itemList.current).toBe('b'); - }); - }); - - describe('.next', () => - { - it('should return the second item for a new list', () => - { - const itemList = createList(); - - expect(itemList.next).toBe('b'); - }); - - it('should return the third item after a step', () => - { - const itemList = createList(); - itemList.step(); - - expect(itemList.next).toBe('c'); - }); - }); - - describe('.previous', () => - { - it('should return undefined for a new list', () => - { - const itemList = createList(); - - expect(itemList.previous).toBe(undefined); - }); - - it('should return the first item after a step', () => - { - const itemList = createList(); - itemList.step(); - - expect(itemList.previous).toBe('a'); - }); - }); - - describe('.eol', () => - { - it('should be false for a new list', () => - { - const itemList = createList(); - - expect(itemList.eol).toBe(false); - }); - - it('should be true for at the end of the list', () => - { - const itemList = createList(); - itemList.step(10); - - expect(itemList.eol).toBe(true); - }); - }); - - describe('.notAtEnd()', () => - { - it('should be true for a new list', () => - { - const itemList = createList(); - const result = itemList.notAtEnd(); - - expect(result).toBe(true); - }); - - it('should be false for at the end of the list', () => - { - const itemList = createList(); - itemList.step(10); - - const result = itemList.notAtEnd(); - - expect(result).toBe(false); - }); - }); - - describe('.step(amount)', () => - { - it('should step one position without an argument', () => - { - const itemList = createList(); - itemList.step(); - - expect(itemList.position).toBe(1); - }); - - it('should step the given amount', () => - { - const itemList = createList(); - itemList.step(5); - - expect(itemList.position).toBe(5); - }); - }); - - describe('.stepBack(amount)', () => - { - it('should step back one position without an argument', () => - { - const itemList = createList(); - itemList.step(5); - itemList.stepBack(); - - expect(itemList.position).toBe(4); - }); - - it('should step back the given amount', () => - { - const itemList = createList(); - itemList.step(5); - itemList.stepBack(3); - - expect(itemList.position).toBe(2); - }); - }); -}); diff --git a/packages/runtime/README.md b/packages/runtime/README.md index f3060c1a..8bb97320 100644 --- a/packages/runtime/README.md +++ b/packages/runtime/README.md @@ -1,7 +1,7 @@ # Jitar Runtime -This package contains the runtime components for [Jitar](https://jitar.dev) applications. +This package provides the client and server for the [Jitar](https://jitar.dev) runtime. For more information about Jitar: diff --git a/packages/runtime/package.json b/packages/runtime/package.json index 42c9df46..7b06f68e 100644 --- a/packages/runtime/package.json +++ b/packages/runtime/package.json @@ -1,44 +1,27 @@ { "name": "@jitar/runtime", - "version": "0.7.5", - "description": "JavaScript runtime library for the Jitar runtime.", + "version": "0.7.4", + "description": "Client and server runtime library for the Jitar runtime.", "author": "Masking Technology (https://jitar.dev)", "license": "MIT", "type": "module", - "types": "dist/lib.d.ts", - "exports": { - ".": "./dist/lib.js" - }, - "files": [ - "CHANGELOG.md", - "README.md", - "dist" - ], - "publishConfig": { - "access": "public" - }, + "types": "dist/index.d.ts", + "exports": "./dist/index.js", + "private": true, "scripts": { "test": "vitest run", "test-coverage": "vitest run --coverage", "lint": "eslint . --ext .ts", "build": "tsc -p tsconfig.json", - "clean": "rm -rf dist", - "prepublishOnly": "npm run clean && npm run build" + "clean": "rm -rf dist" }, "dependencies": { - "@jitar/serialization": "*" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/MaskingTechnology/jitar.git" - }, - "bugs": { - "url": "https://github.com/MaskingTechnology/jitar/issues" - }, - "homepage": "https://jitar.dev", - "keywords": [ - "javascript", - "runtime", - "jitar" - ] + "@jitar/configuration": "*", + "@jitar/execution": "*", + "@jitar/health": "*", + "@jitar/middleware": "*", + "@jitar/services": "*", + "@jitar/serialization": "*", + "@jitar/sourcing": "*" + } } diff --git a/packages/runtime/src/ProcedureRunner.ts b/packages/runtime/src/ProcedureRunner.ts new file mode 100644 index 00000000..2679f2ca --- /dev/null +++ b/packages/runtime/src/ProcedureRunner.ts @@ -0,0 +1,19 @@ + +import type { Runner, Request, Response } from '@jitar/execution'; +import type { Middleware, NextHandler } from '@jitar/middleware'; + +export default class ProcedureRunner implements Middleware +{ + #runner: Runner; + + constructor(runner: Runner) + { + this.#runner = runner; + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + async handle(request: Request, next: NextHandler): Promise + { + return this.#runner.run(request); + } +} diff --git a/packages/runtime/src/Runtime.ts b/packages/runtime/src/Runtime.ts new file mode 100644 index 00000000..3ffce5a7 --- /dev/null +++ b/packages/runtime/src/Runtime.ts @@ -0,0 +1,51 @@ + +import { Request, Response, RunModes, StatusCodes, VersionParser, ProcedureNotAccessible } from '@jitar/execution'; + +export default abstract class Runtime +{ + #versionParser = new VersionParser(); + + constructor() + { + this.#initGlobals(); + } + + #initGlobals(): void + { + // These globals are only used by the remote implementations. + // The reason we use them is to decouple the runtime from the remote implementations. + // This way, remote implementations can be bundled separately from Jitar. + + const globals = globalThis as Record; + globals.__run = this.#run.bind(this); + globals.ProcedureNotAccessible = ProcedureNotAccessible; + } + + abstract getTrustKey(): string | undefined; + + abstract runInternal(request: Request): Promise; + + async #run(fqn: string, versionNumber: string, args: Record, sourceRequest?: Request): Promise + { + const version = this.#versionParser.parse(versionNumber); + const argsMap = new Map(Object.entries(args)); + const headersMap = sourceRequest instanceof Request ? sourceRequest.headers : new Map(); + + const targetRequest = new Request(fqn, version, argsMap, headersMap, RunModes.NORMAL); + const trustKey = this.getTrustKey(); + + if (trustKey !== undefined) + { + targetRequest.setHeader('X-Jitar-Trust-Key', trustKey); + } + + const targetResponse = await this.runInternal(targetRequest); + + if (targetResponse.status !== StatusCodes.OK) + { + throw targetResponse.result; + } + + return targetResponse.result; + } +} diff --git a/packages/runtime/src/RuntimeBuilder.ts b/packages/runtime/src/RuntimeBuilder.ts deleted file mode 100644 index c41cb1bf..00000000 --- a/packages/runtime/src/RuntimeBuilder.ts +++ /dev/null @@ -1,202 +0,0 @@ - -import RuntimeNotBuilt from './errors/RuntimeNotBuilt.js'; - -import FileManager from './interfaces/FileManager.js'; - -import RemoteRepository from './services/RemoteRepository.js'; -import LocalRepository from './services/LocalRepository.js'; - -import RemoteGateway from './services/RemoteGateway.js'; -import LocalGateway from './services/LocalGateway.js'; - -import RemoteWorker from './services/RemoteWorker.js'; -import LocalWorker from './services/LocalWorker.js'; - -import Proxy from './services/Proxy.js'; -import Standalone from './services/Standalone.js'; - -export default class RuntimeBuilder -{ - #url?: string; - #fileManager?: FileManager; - #segments: Set = new Set(); - #healthChecks: Set = new Set(); - #middlewares: Set = new Set(); - #assets: Set = new Set(); - #overrides: Map = new Map(); - - #repository?: RemoteRepository; - #gateway?: RemoteGateway; - #worker?: RemoteWorker; - - url(url?: string): this - { - this.#url = url; - - return this; - } - - fileManager(fileManager: FileManager): this - { - this.#fileManager = fileManager; - - return this; - } - - segment(...names: string[]): this - { - names.forEach(name => this.#segments.add(name)); - - return this; - } - - healthCheck(...filenames: string[]): this - { - filenames.forEach(filename => this.#healthChecks.add(filename)); - - return this; - } - - middleware(...filenames: string[]): this - { - filenames.forEach(filename => this.#middlewares.add(filename)); - - return this; - } - - asset(...patterns: string[]): this - { - patterns.forEach(pattern => this.#assets.add(pattern)); - - return this; - } - - override(...mappings: Record[]): this - { - for (const map of mappings) - { - for (const [key, value] of Object.entries(map)) - { - this.#overrides.set(key, value); - } - } - - return this; - } - - repository(url?: string): this - { - this.#repository = url !== undefined ? new RemoteRepository(url) : undefined; - - return this; - } - - gateway(url?: string): this - { - this.#gateway = url !== undefined ? new RemoteGateway(url) : undefined; - - return this; - } - - worker(url?: string): this - { - this.#worker = url !== undefined ? new RemoteWorker(url) : undefined; - - return this; - } - - buildRepository(): LocalRepository - { - if (this.#fileManager === undefined) - { - throw new RuntimeNotBuilt('File manager is not set for the repository'); - } - - const repository = new LocalRepository(this.#fileManager, this.#url); - repository.healthCheckFiles = this.#healthChecks; - repository.segmentNames = this.#segments; - repository.assets = this.#assets; - repository.overrides = this.#overrides; - - return repository; - } - - buildGateway(trustKey?: string): LocalGateway - { - if (this.#repository === undefined) - { - throw new RuntimeNotBuilt('Repository is not set for the gateway'); - } - - const gateway = new LocalGateway(this.#repository, this.#url, trustKey); - gateway.healthCheckFiles = this.#healthChecks; - gateway.middlewareFiles = this.#middlewares; - - return gateway; - } - - buildWorker(trustKey?: string): LocalWorker - { - if (this.#repository === undefined) - { - throw new RuntimeNotBuilt('Repository is not set for the worker'); - } - - const worker = new LocalWorker(this.#repository, this.#gateway, this.#url, trustKey); - worker.segmentNames = this.#segments; - worker.healthCheckFiles = this.#healthChecks; - worker.middlewareFiles = this.#middlewares; - - this.#repository.segmentNames = this.#segments; - - if (this.gateway !== undefined) - { - (this.#gateway as RemoteGateway).worker = worker; - } - - return worker; - } - - buildProxy(): Proxy - { - if (this.#repository === undefined) - { - throw new RuntimeNotBuilt('Repository is not set for the proxy'); - } - - const runner = this.#gateway ?? this.#worker; - - if (runner === undefined) - { - throw new RuntimeNotBuilt('Runner (gateway or worker) is not set for the proxy'); - } - - const proxy = new Proxy(this.#repository, runner, this.#url); - proxy.healthCheckFiles = this.#healthChecks; - proxy.middlewareFiles = this.#middlewares; - - return proxy; - } - - buildStandalone(trustKey?: string): Standalone - { - if (this.#fileManager === undefined) - { - throw new RuntimeNotBuilt('File manager is not set for the standalone'); - } - - const repository = new LocalRepository(this.#fileManager, this.#url); - repository.segmentNames = this.#segments; - repository.assets = this.#assets; - repository.overrides = this.#overrides; - - const worker = new LocalWorker(repository, this.#gateway, this.#url, trustKey); - worker.segmentNames = this.#segments; - - const standalone = new Standalone(repository, worker, this.#url); - standalone.healthCheckFiles = this.#healthChecks; - standalone.middlewareFiles = this.#middlewares; - - return standalone; - } -} diff --git a/packages/runtime/src/client.ts b/packages/runtime/src/client.ts deleted file mode 100644 index c3c4d956..00000000 --- a/packages/runtime/src/client.ts +++ /dev/null @@ -1,39 +0,0 @@ - -import LocalWorker from './services/LocalWorker.js'; -import RemoteGateway from './services/RemoteGateway.js'; -import RemoteRepository from './services/RemoteRepository.js'; - -let client: LocalWorker | undefined = undefined; -const resolvers: ((client: LocalWorker) => void)[] = []; - -export async function startClient(remoteUrl: string, segmentNames: string[] = [], middlewares: string[] = []): Promise -{ - const repository = new RemoteRepository(remoteUrl); - const gateway = new RemoteGateway(remoteUrl); - - const worker = new LocalWorker(repository, gateway); - worker.segmentNames = new Set(segmentNames); - worker.middlewareFiles = new Set(middlewares); - - await worker.start(); - - client = worker; - - resolvers.forEach((resolve) => resolve(worker)); - resolvers.length = 0; - - return worker; -} - -export async function getClient(): Promise -{ - if (client === undefined) - { - return new Promise((resolve) => - { - resolvers.push(resolve); - }); - } - - return client; -} diff --git a/packages/runtime/src/client/Client.ts b/packages/runtime/src/client/Client.ts new file mode 100644 index 00000000..fab73554 --- /dev/null +++ b/packages/runtime/src/client/Client.ts @@ -0,0 +1,67 @@ + +import type { ExecutionManager, Request, Response } from '@jitar/execution'; +import type { MiddlewareManager } from '@jitar/middleware'; +import { LocalWorker, RemoteGateway, Remote } from '@jitar/services'; + +import ProcedureRunner from '../ProcedureRunner'; +import Runtime from '../Runtime'; + +type Configuration = +{ + remoteUrl: string; + remote: Remote; + middlewareManager: MiddlewareManager; + executionManager: ExecutionManager; +}; + +export default class Client extends Runtime +{ + #worker: LocalWorker; + #middlewareManager: MiddlewareManager; + + constructor(configuration: Configuration) + { + super(); + + this.#worker = new LocalWorker({ + url: configuration.remoteUrl, + gateway: new RemoteGateway({ + url: configuration.remoteUrl, + remote: configuration.remote + }), + executionManager: configuration.executionManager + }); + + this.#middlewareManager = configuration.middlewareManager; + + const procedureRunner = new ProcedureRunner(this.#worker); + this.#middlewareManager.addMiddleware(procedureRunner); + } + + get worker() { return this.#worker; } + + start(): Promise + { + return this.#worker.start(); + } + + stop(): Promise + { + return this.#worker.stop(); + } + + getTrustKey(): string | undefined + { + return undefined; + } + + run(request: Request): Promise + { + return this.runInternal(request); + } + + runInternal(request: Request): Promise + { + return this.#middlewareManager.handle(request); + } +} diff --git a/packages/runtime/src/client/ClientBuilder.ts b/packages/runtime/src/client/ClientBuilder.ts new file mode 100644 index 00000000..ef594af2 --- /dev/null +++ b/packages/runtime/src/client/ClientBuilder.ts @@ -0,0 +1,51 @@ + +import { Segment, ExecutionManager } from '@jitar/execution'; +import { RemoteBuilder } from '@jitar/services'; +import { Middleware, MiddlewareManager } from '@jitar/middleware'; + +import Client from './Client'; + +type ClientConfiguration = +{ + remoteUrl: string; + middleware?: Middleware[]; + segments?: Segment[]; +}; + +export default class ClientBuilder +{ + #remoteBuilder: RemoteBuilder; + + constructor(remoteBuilder: RemoteBuilder) + { + this.#remoteBuilder = remoteBuilder; + } + + build(configuration: ClientConfiguration): Client + { + const remoteUrl = configuration.remoteUrl; + const remote = this.#remoteBuilder.build(remoteUrl); + const middlewareManager = this.#buildMiddlewareManager(configuration.middleware ?? []); + const executionManager = this.#buildExecutionManager(configuration.segments ?? []); + + return new Client({ remoteUrl, remote, middlewareManager, executionManager }); + } + + #buildMiddlewareManager(middleware: Middleware[]): MiddlewareManager + { + const manager = new MiddlewareManager(); + + middleware.forEach(middleware => manager.addMiddleware(middleware)); + + return manager; + } + + #buildExecutionManager(segments: Segment[]): ExecutionManager + { + const manager = new ExecutionManager(); + + segments.forEach(segment => manager.addSegment(segment)); + + return manager; + } +} diff --git a/packages/runtime/src/definitions/ExecutionScope.ts b/packages/runtime/src/definitions/ExecutionScope.ts deleted file mode 100644 index c9133170..00000000 --- a/packages/runtime/src/definitions/ExecutionScope.ts +++ /dev/null @@ -1,13 +0,0 @@ - -const ExecutionScopes = -{ - APPLICATION: 'application', - RUNTIME: 'runtime' -} as const; - -Object.freeze(ExecutionScopes); - -type Keys = keyof typeof ExecutionScopes; -type ExecutionScope = typeof ExecutionScopes[Keys]; - -export { ExecutionScope, ExecutionScopes }; diff --git a/packages/runtime/src/definitions/Files.ts b/packages/runtime/src/definitions/Files.ts deleted file mode 100644 index da894e94..00000000 --- a/packages/runtime/src/definitions/Files.ts +++ /dev/null @@ -1,39 +0,0 @@ - -const Files = -{ - MODULE_PATTERN: '**/*.js', - SEGMENT_PATTERN: '**/*.segment.json', - WORKER_SEGMENT_PATTERN: '**/*.segment.worker.js', - REPOSITORY_SEGMENT_PATTERN: '**/*.segment.repository.js' -}; - -Object.freeze(Files); - -const EXTENSION_PATTERN = /\.js$/; - -function convertToLocalFilename(filename: string): string -{ - return filename.replace(EXTENSION_PATTERN, '.local.js'); -} - -function convertToRemoteFilename(filename: string): string -{ - return filename.replace(EXTENSION_PATTERN, '.remote.js'); -} - -function createWorkerFilename(name: string): string -{ - return `./${name}.segment.worker.js`; -} - -function createRepositoryFilename(name: string): string -{ - return `./${name}.segment.repository.js`; -} - -function isSegmentFilename(filename: string): boolean -{ - return filename.includes('.segment.'); -} - -export { Files, convertToLocalFilename, convertToRemoteFilename, createWorkerFilename, createRepositoryFilename, isSegmentFilename }; diff --git a/packages/runtime/src/errors/ClientNotFound.ts b/packages/runtime/src/errors/ClientNotFound.ts deleted file mode 100644 index e5597e04..00000000 --- a/packages/runtime/src/errors/ClientNotFound.ts +++ /dev/null @@ -1,20 +0,0 @@ - -import { Loadable } from '@jitar/serialization'; - -import BadRequest from './generic/BadRequest.js'; - -export default class ClientNotFound extends BadRequest -{ - #clientId: string; - - constructor(clientId: string) - { - super(`Client not found for id '${clientId}'`); - - this.#clientId = clientId; - } - - get clientId() { return this.#clientId; } -} - -(ClientNotFound as Loadable).source = 'RUNTIME_ERROR_LOCATION'; diff --git a/packages/runtime/src/errors/InvalidClientId.ts b/packages/runtime/src/errors/InvalidClientId.ts deleted file mode 100644 index fa24e167..00000000 --- a/packages/runtime/src/errors/InvalidClientId.ts +++ /dev/null @@ -1,20 +0,0 @@ - -import { Loadable } from '@jitar/serialization'; - -import BadRequest from './generic/BadRequest.js'; - -export default class InvalidClientId extends BadRequest -{ - #clientId: string; - - constructor(clientId: string) - { - super(`Invalid client id '${clientId}'`); - - this.#clientId = clientId; - } - - get clientId() { return this.#clientId; } -} - -(InvalidClientId as Loadable).source = 'RUNTIME_ERROR_LOCATION'; diff --git a/packages/runtime/src/errors/InvalidHealthCheck.ts b/packages/runtime/src/errors/InvalidHealthCheck.ts deleted file mode 100644 index 36076bd2..00000000 --- a/packages/runtime/src/errors/InvalidHealthCheck.ts +++ /dev/null @@ -1,20 +0,0 @@ - -import { Loadable } from '@jitar/serialization'; - -import ServerError from './generic/ServerError.js'; - -export default class InvalidHealthCheck extends ServerError -{ - #url: string; - - constructor(url: string) - { - super(`Module '${url}' does not export a valid health check`); - - this.#url = url; - } - - get url() { return this.#url; } -} - -(InvalidHealthCheck as Loadable).source = 'RUNTIME_ERROR_LOCATION'; diff --git a/packages/runtime/src/errors/InvalidMiddleware.ts b/packages/runtime/src/errors/InvalidMiddleware.ts deleted file mode 100644 index c81000d1..00000000 --- a/packages/runtime/src/errors/InvalidMiddleware.ts +++ /dev/null @@ -1,20 +0,0 @@ - -import { Loadable } from '@jitar/serialization'; - -import ServerError from './generic/ServerError.js'; - -export default class InvalidMiddleware extends ServerError -{ - #url: string; - - constructor(url: string) - { - super(`Module '${url}' does not export valid middleware`); - - this.#url = url; - } - - get url() { return this.#url; } -} - -(InvalidMiddleware as Loadable).source = 'RUNTIME_ERROR_LOCATION'; diff --git a/packages/runtime/src/errors/InvalidSegmentFile.ts b/packages/runtime/src/errors/InvalidSegmentFile.ts deleted file mode 100644 index 56099e12..00000000 --- a/packages/runtime/src/errors/InvalidSegmentFile.ts +++ /dev/null @@ -1,20 +0,0 @@ - -import { Loadable } from '@jitar/serialization'; - -import ServerError from './generic/ServerError.js'; - -export default class InvalidSegmentFile extends ServerError -{ - #filename: string; - - constructor(filename: string) - { - super(`Missing files array in segment file '${filename}'`); - - this.#filename = filename; - } - - get filename() { return this.#filename; } -} - -(InvalidSegmentFile as Loadable).source = 'RUNTIME_ERROR_LOCATION'; diff --git a/packages/runtime/src/errors/InvalidTrustKey.ts b/packages/runtime/src/errors/InvalidTrustKey.ts deleted file mode 100644 index 141e62a3..00000000 --- a/packages/runtime/src/errors/InvalidTrustKey.ts +++ /dev/null @@ -1,14 +0,0 @@ - -import { Loadable } from '@jitar/serialization'; - -import Unauthorized from './generic/Unauthorized.js'; - -export default class InvalidTrustKey extends Unauthorized -{ - constructor() - { - super(`Invalid trust key`); - } -} - -(InvalidTrustKey as Loadable).source = 'RUNTIME_ERROR_LOCATION'; diff --git a/packages/runtime/src/errors/ModuleNotAccessible.ts b/packages/runtime/src/errors/ModuleNotAccessible.ts deleted file mode 100644 index a4911551..00000000 --- a/packages/runtime/src/errors/ModuleNotAccessible.ts +++ /dev/null @@ -1,20 +0,0 @@ - -import { Loadable } from '@jitar/serialization'; - -import Forbidden from './generic/Forbidden.js'; - -export default class ModuleNotAccessible extends Forbidden -{ - #url: string; - - constructor(url: string) - { - super(`Module '${url}' is not accessible`); - - this.#url = url; - } - - get url() { return this.#url; } -} - -(ModuleNotAccessible as Loadable).source = 'RUNTIME_ERROR_LOCATION'; diff --git a/packages/runtime/src/errors/ProcedureNotAccessible.ts b/packages/runtime/src/errors/ProcedureNotAccessible.ts deleted file mode 100644 index f4581e79..00000000 --- a/packages/runtime/src/errors/ProcedureNotAccessible.ts +++ /dev/null @@ -1,24 +0,0 @@ - -import { Loadable } from '@jitar/serialization'; - -import Forbidden from './generic/Forbidden.js'; - -export default class ProcedureNotAccessible extends Forbidden -{ - #fqn: string; - #versionNumber: string; - - constructor(fqn: string, versionNumber: string) - { - super(`Procedure '${fqn}' (v${versionNumber}) is not accessible`); - - this.#fqn = fqn; - this.#versionNumber = versionNumber; - } - - get fqn() { return this.#fqn; } - - get versionNumber() { return this.#versionNumber; } -} - -(ProcedureNotAccessible as Loadable).source = 'RUNTIME_ERROR_LOCATION'; diff --git a/packages/runtime/src/errors/RepositoryNotAvailable.ts b/packages/runtime/src/errors/RepositoryNotAvailable.ts deleted file mode 100644 index 2ceb98d6..00000000 --- a/packages/runtime/src/errors/RepositoryNotAvailable.ts +++ /dev/null @@ -1,14 +0,0 @@ - -import { Loadable } from '@jitar/serialization'; - -import ServerError from './generic/ServerError.js'; - -export default class RepositoryNotAvailable extends ServerError -{ - constructor() - { - super(`Repository not available`); - } -} - -(RepositoryNotAvailable as Loadable).source = 'RUNTIME_ERROR_LOCATION'; diff --git a/packages/runtime/src/errors/RuntimeNotAvailable.ts b/packages/runtime/src/errors/RuntimeNotAvailable.ts deleted file mode 100644 index b66c51b4..00000000 --- a/packages/runtime/src/errors/RuntimeNotAvailable.ts +++ /dev/null @@ -1,14 +0,0 @@ - -import { Loadable } from '@jitar/serialization'; - -import ServerError from './generic/ServerError.js'; - -export default class RuntimeNotAvailable extends ServerError -{ - constructor() - { - super(`Runtime not available`); - } -} - -(RuntimeNotAvailable as Loadable).source = 'RUNTIME_ERROR_LOCATION'; diff --git a/packages/runtime/src/errors/RuntimeNotBuilt.ts b/packages/runtime/src/errors/RuntimeNotBuilt.ts deleted file mode 100644 index f7f0b9f9..00000000 --- a/packages/runtime/src/errors/RuntimeNotBuilt.ts +++ /dev/null @@ -1,14 +0,0 @@ - -import { Loadable } from '@jitar/serialization'; - -import ServerError from './generic/ServerError.js'; - -export default class RuntimeNotBuilt extends ServerError -{ - constructor(reason: string) - { - super(`Building the runtime failed: ${reason}`); - } -} - -(RuntimeNotBuilt as Loadable).source = 'RUNTIME_ERROR_LOCATION'; diff --git a/packages/runtime/src/errors/SegmentNotFound.ts b/packages/runtime/src/errors/SegmentNotFound.ts deleted file mode 100644 index 74ab3be1..00000000 --- a/packages/runtime/src/errors/SegmentNotFound.ts +++ /dev/null @@ -1,20 +0,0 @@ - -import { Loadable } from '@jitar/serialization'; - -import ServerError from './generic/ServerError.js'; - -export default class SegmentNotFound extends ServerError -{ - #source: string; - - constructor(source: string) - { - super(`Segment found for '${source}'`); - - this.#source = source; - } - - get source() { return this.#source; } -} - -(SegmentNotFound as Loadable).source = 'RUNTIME_ERROR_LOCATION'; diff --git a/packages/runtime/src/errors/generic/Teapot.ts b/packages/runtime/src/errors/generic/Teapot.ts deleted file mode 100644 index 811ac3e6..00000000 --- a/packages/runtime/src/errors/generic/Teapot.ts +++ /dev/null @@ -1,12 +0,0 @@ - -import { Loadable } from '@jitar/serialization'; - -export default class Teapot extends Error -{ - constructor() - { - super(`I'm a teapot`); - } -} - -(Teapot as Loadable).source = 'RUNTIME_ERROR_LOCATION'; diff --git a/packages/runtime/src/globals.ts b/packages/runtime/src/globals.ts deleted file mode 100644 index 5216c1ff..00000000 --- a/packages/runtime/src/globals.ts +++ /dev/null @@ -1,19 +0,0 @@ - -import ProcedureNotAccessible from './errors/ProcedureNotAccessible.js'; - -import { importModule, runProcedure } from './hooks.js'; - -declare global -{ - const __import: typeof importModule; - const __run: typeof runProcedure; -} - -export const globals = globalThis as Record; - -// Available for external use -globals.__import = importModule; -globals.__run = runProcedure; - -// Internal use only -globals.ProcedureNotAccessible = ProcedureNotAccessible; diff --git a/packages/runtime/src/hooks.ts b/packages/runtime/src/hooks.ts deleted file mode 100644 index 0d2d2dc9..00000000 --- a/packages/runtime/src/hooks.ts +++ /dev/null @@ -1,53 +0,0 @@ - -import { ExecutionScope } from './definitions/ExecutionScope.js'; -import RuntimeNotAvailable from './errors/RuntimeNotAvailable.js'; -import Request from './models/Request.js'; -import ProcedureRuntime from './services/ProcedureRuntime.js'; -import VersionParser from './utils/VersionParser.js'; - -const RUNS_IN_BROWSER = typeof window !== 'undefined'; - -let _runtime: ProcedureRuntime; - -export function setRuntime(runtime: ProcedureRuntime): void -{ - _runtime = runtime; -} - -export function getRuntime(): ProcedureRuntime -{ - if (_runtime === undefined) - { - throw new RuntimeNotAvailable(); - } - - return _runtime; -} - -export async function importModule(name: string, scope: ExecutionScope, extractDefault = true): Promise -{ - const runtime = getRuntime(); - - if (RUNS_IN_BROWSER && name === 'JITAR_LIBRARY_NAME') - { - name = 'RUNTIME_HOOKS_LOCATION'; - } - - const module = await runtime.import(name, scope); - - return extractDefault && module.default !== undefined ? module.default : module; -} - -export async function runProcedure(fqn: string, versionNumber: string, args: object, sourceRequest?: Request): Promise -{ - const runtime = getRuntime(); - - const version = VersionParser.parse(versionNumber); - const argsMap = new Map(Object.entries(args)); - const headersMap = sourceRequest instanceof Request ? sourceRequest.headers : new Map(); - - const targetRequest = new Request(fqn, version, argsMap, headersMap); - const targetResponse = await runtime.handle(targetRequest); - - return targetResponse.result; -} diff --git a/packages/runtime/src/index.ts b/packages/runtime/src/index.ts new file mode 100644 index 00000000..c229a27d --- /dev/null +++ b/packages/runtime/src/index.ts @@ -0,0 +1,8 @@ + +export { default as Client } from './client/Client'; +export { default as ClientBuilder } from './client/ClientBuilder'; + +export { default as ContentTypes } from './server/definitions/ContentTypes'; +export { default as ServerResponse } from './server/types/ServerResponse'; +export { default as Server } from './server/Server'; +export { default as ServerBuilder } from './server/ServerBuilder'; diff --git a/packages/runtime/src/interfaces/Middleware.ts b/packages/runtime/src/interfaces/Middleware.ts deleted file mode 100644 index ea5c0b3d..00000000 --- a/packages/runtime/src/interfaces/Middleware.ts +++ /dev/null @@ -1,12 +0,0 @@ - -import Request from '../models/Request.js'; -import Response from '../models/Response.js'; - -import NextHandler from '../types/NextHandler.js'; - -interface Middleware -{ - handle(request: Request, next: NextHandler): Promise; -} - -export default Middleware; diff --git a/packages/runtime/src/interfaces/Runner.ts b/packages/runtime/src/interfaces/Runner.ts deleted file mode 100644 index 8387c886..00000000 --- a/packages/runtime/src/interfaces/Runner.ts +++ /dev/null @@ -1,10 +0,0 @@ - -import Request from '../models/Request.js'; -import Response from '../models/Response.js'; - -interface Runner -{ - run(request: Request): Promise; -} - -export default Runner; diff --git a/packages/runtime/src/lib.ts b/packages/runtime/src/lib.ts deleted file mode 100644 index 2a4f5159..00000000 --- a/packages/runtime/src/lib.ts +++ /dev/null @@ -1,83 +0,0 @@ - -// Definitions -export * from './definitions/AccessLevel.js'; -export * from './definitions/Files.js'; -export * from './definitions/ExecutionScope.js'; - -// Generic errors -export { default as BadRequest } from './errors/generic/BadRequest.js'; -export { default as Forbidden } from './errors/generic/Forbidden.js'; -export { default as NotFound } from './errors/generic/NotFound.js'; -export { default as NotImplemented } from './errors/generic/NotImplemented.js'; -export { default as PaymentRequired } from './errors/generic/PaymentRequired.js'; -export { default as ServerError } from './errors/generic/ServerError.js'; -export { default as Teapot } from './errors/generic/Teapot.js'; -export { default as Unauthorized } from './errors/generic/Unauthorized.js'; - -// Runtime errors -export { default as ClientNotFound } from './errors/ClientNotFound.js'; -export { default as FileNotFound } from './errors/FileNotFound.js'; -export { default as ImplementationNotFound } from './errors/ImplementationNotFound.js'; -export { default as InvalidClientId } from './errors/InvalidClientId.js'; -export { default as InvalidParameterValue } from './errors/InvalidParameterValue.js'; -export { default as InvalidSegmentFile } from './errors/InvalidSegmentFile.js'; -export { default as InvalidVersionNumber } from './errors/InvalidVersionNumber.js'; -export { default as MissingParameterValue } from './errors/MissingParameterValue.js'; -export { default as ModuleNotAccessible } from './errors/ModuleNotAccessible.js'; -export { default as ModuleNotLoaded } from './errors/ModuleNotLoaded.js'; -export { default as NoWorkerAvailable } from './errors/NoWorkerAvailable.js'; -export { default as ProcedureNotAccessible } from './errors/ProcedureNotAccessible.js'; -export { default as ProcedureNotFound } from './errors/ProcedureNotFound.js'; -export { default as RepositoryNotAvailable } from './errors/RepositoryNotAvailable.js'; -export { default as RuntimeNotAvailable } from './errors/RuntimeNotAvailable.js'; -export { default as SegmentNotFound } from './errors/SegmentNotFound.js'; -export { default as UnknownParameter } from './errors/UnknownParameter.js'; - -// Interfaces -export { default as FileManager } from './interfaces/FileManager.js'; -export { default as HealthCheck } from './interfaces/HealthCheck.js'; -export { default as Middleware } from './interfaces/Middleware.js'; - -// Models -export { default as ArrayParameter } from './models/ArrayParameter.js'; -export { default as File } from './models/File.js'; -export { default as Implementation } from './models/Implementation.js'; -export { default as NamedParameter } from './models/NamedParameter.js'; -export { default as ObjectParameter } from './models/ObjectParameter.js'; -export { default as Procedure } from './models/Procedure.js'; -export { default as Request } from './models/Request.js'; -export { default as Response } from './models/Response.js'; -export { default as Segment } from './models/Segment.js'; -export { default as Version } from './models/Version.js'; - -// Services -export { default as Gateway } from './services/Gateway.js'; -export { default as LocalGateway } from './services/LocalGateway.js'; -export { default as LocalWorker } from './services/LocalWorker.js'; -export { default as LocalRepository } from './services/LocalRepository.js'; -export { default as Worker } from './services/Worker.js'; -export { default as WorkerMonitor } from './services/WorkerMonitor.js'; -export { default as ProcedureRuntime } from './services/ProcedureRuntime.js'; -export { default as Proxy } from './services/Proxy.js'; -export { default as Remote } from './services/Remote.js'; -export { default as RemoteWorker } from './services/RemoteWorker.js'; -export { default as RemoteGateway } from './services/RemoteGateway.js'; -export { default as RemoteRepository } from './services/RemoteRepository.js'; -export { default as Repository } from './services/Repository.js'; -export { default as Runtime } from './services/Runtime.js'; -export { default as Standalone } from './services/Standalone.js'; - -// Types -export { default as NextHandler } from './types/NextHandler.js'; -export { default as ModuleImporter } from './types/ModuleImporter.js'; - -// Utils -export { default as ClientIdHelper } from './utils/ClientIdHelper.js'; -export { default as ModuleLoader } from './utils/ModuleLoader.js'; -export { default as RemoteClassLoader } from './utils/RemoteClassLoader.js'; -export { default as VersionParser } from './utils/VersionParser.js'; - -// Root -export { default as RuntimeBuilder } from './RuntimeBuilder.js'; -export * from './client.js'; -import './globals.js'; diff --git a/packages/runtime/src/models/Segment.ts b/packages/runtime/src/models/Segment.ts deleted file mode 100644 index ccca983d..00000000 --- a/packages/runtime/src/models/Segment.ts +++ /dev/null @@ -1,41 +0,0 @@ - -import Procedure from './Procedure.js'; - -export default class Segment -{ - #id: string; - #procedures: Map = new Map(); - - constructor(id: string) - { - this.#id = id; - } - - get id() { return this.#id; } - - addProcedure(procedure: Procedure): Segment - { - this.#procedures.set(procedure.fqn, procedure); - - return this; - } - - hasProcedure(fqn: string): boolean - { - const procedure = this.getProcedure(fqn); - - return procedure !== undefined; - } - - getProcedure(fqn: string): Procedure | undefined - { - return this.#procedures.get(fqn); - } - - getExposedProcedures(): Procedure[] - { - const procedures = [...this.#procedures.values()]; - - return procedures.filter(procedure => procedure.public || procedure.protected); - } -} diff --git a/packages/runtime/src/server/Server.ts b/packages/runtime/src/server/Server.ts new file mode 100644 index 00000000..03d7680f --- /dev/null +++ b/packages/runtime/src/server/Server.ts @@ -0,0 +1,385 @@ + +import { BadRequest, Forbidden, NotFound, NotImplemented, PaymentRequired, Teapot, Unauthorized } from '@jitar/errors'; +import { Request, Version, VersionParser } from '@jitar/execution'; +import type { Response } from '@jitar/execution'; +import type { MiddlewareManager } from '@jitar/middleware'; +import type { HealthManager } from '@jitar/health'; +import type { File, SourcingManager } from '@jitar/sourcing'; +import { LocalGateway, LocalWorker, RemoteWorker, Proxy, RemoteBuilder } from '@jitar/services'; +import { Logger } from '@jitar/logging'; + +import ProcedureRunner from '../ProcedureRunner'; +import Runtime from '../Runtime'; + +import ContentTypes from './definitions/ContentTypes'; +import StatusCodes from './definitions/StatusCodes'; + +import type ServerResponse from './types/ServerResponse'; +import type AddWorkerRequest from './types/AddWorkerRequest'; +import type ProvideRequest from './types/ProvideRequest'; +import type RunRequest from './types/RunRequest'; + +type Configuration = +{ + proxy: Proxy; + sourcingManager: SourcingManager; + remoteBuilder: RemoteBuilder; + middlewareManager: MiddlewareManager; + healthManager: HealthManager; + setUpScripts?: string[]; + tearDownScripts?: string[]; +}; + +export default class Server extends Runtime +{ + #proxy: Proxy; + #sourcingManager: SourcingManager; + #remoteBuilder: RemoteBuilder; + #middlewareManager: MiddlewareManager; + #healthManager: HealthManager; + #setUpScripts: string[]; + #tearDownScripts: string[]; + + #logger: Logger = new Logger(); + #versionParser = new VersionParser(); + + constructor(configuration: Configuration) + { + super(); + + this.#proxy = configuration.proxy; + this.#sourcingManager = configuration.sourcingManager; + this.#remoteBuilder = configuration.remoteBuilder; + this.#middlewareManager = configuration.middlewareManager; + this.#healthManager = configuration.healthManager; + this.#setUpScripts = configuration.setUpScripts ?? []; + this.#tearDownScripts = configuration.tearDownScripts ?? []; + + const procedureRunner = new ProcedureRunner(this.#proxy); + this.#middlewareManager.addMiddleware(procedureRunner); + } + + get proxy() { return this.#proxy; } + + getTrustKey(): string | undefined + { + return this.#proxy.trustKey; + } + + async start(): Promise + { + await this.#setUp(); + + await this.#proxy.start(); + + this.#logger.info(`Server started at ${this.#proxy.url}`); + + if (this.#proxy.runner instanceof LocalWorker) + { + this.#logger.info('RPC procedures:', this.#proxy.runner.getProcedureNames()); + } + } + + async stop(): Promise + { + await this.#proxy.stop(); + + await this.#tearDown(); + + this.#logger.info('Server stopped'); + } + + async getHealth(): Promise + { + try + { + const internalHealth = await this.#proxy.getHealth(); + const externalHealth = await this.#healthManager.getHealth(); + const health = new Map([...internalHealth, ...externalHealth]); + + this.#logger.info('Got health'); + + return this.#respondHealth(health); + } + catch (error: unknown) + { + const message = error instanceof Error ? error.message : String(error); + + this.#logger.error('Failed to get health:', message); + + return this.#respondError(error); + } + } + + async isHealthy(): Promise + { + try + { + const internalsHealthy = await this.#proxy.isHealthy(); + const externalsHealthy = await this.#healthManager.isHealthy(); + const healthy = internalsHealthy && externalsHealthy; + + this.#logger.debug('Got health status'); + + return this.#respondHealthy(healthy); + } + catch (error: unknown) + { + const message = error instanceof Error ? error.message : String(error); + + this.#logger.error('Failed to get health status:', message); + + return this.#respondError(error); + } + } + + async provide(provideRequest: ProvideRequest): Promise + { + try + { + const file = await this.#proxy.provide(provideRequest.filename); + + this.#logger.info('Provided file:', provideRequest.filename); + + return this.#respondFile(file); + } + catch (error: unknown) + { + const message = error instanceof Error ? error.message : String(error); + + this.#logger.warn('Failed to provide file:', message); + + return this.#respondError(error); + } + } + + async run(runRequest: RunRequest): Promise + { + try + { + const request = this.#transformRunRequest(runRequest); + + // Middleware is only executed on external requests. + const response = await this.#middlewareManager.handle(request); + + this.#logger.info('Ran request:', request.fqn); + + return this.#respondResponse(response); + } + catch (error: unknown) + { + const message = error instanceof Error ? error.message : String(error); + + this.#logger.error('Failed run request:', message); + + return this.#respondError(error); + } + } + + async runInternal(request: Request): Promise + { + // Middleware is not executed on internal requests. + return this.#proxy.run(request); + } + + async addWorker(addRequest: AddWorkerRequest): Promise + { + try + { + const runner = this.#proxy.runner; + + if ((runner instanceof LocalGateway) === false) + { + throw new BadRequest('Cannot add worker to remote gateway'); + } + + const worker = this.#buildRemoteWorker(addRequest.url, addRequest.procedureNames); + + await runner.addWorker(worker, addRequest.trustKey); + + this.#logger.info('Added worker:', worker.url); + + return this.#respondSuccess(); + } + catch (error: unknown) + { + const message = error instanceof Error ? error.message : String(error); + + this.#logger.error('Failed to add worker:', message); + + return this.#respondError(error); + } + } + + #setUp(): Promise + { + return this.#runScripts(this.#setUpScripts); + } + + #tearDown(): Promise + { + return this.#runScripts(this.#tearDownScripts); + } + + async #runScripts(scripts: string[]): Promise + { + await Promise.all(scripts.map(script => this.#sourcingManager.import(script))); + } + + #transformRunRequest(request: RunRequest): Request + { + const fqn = this.#processFqn(request.fqn); + const version = this.#parseVersion(request.version); + const args = this.#mapArguments(request.args); + const headers = this.#mapHeaders(request.headers); + + return new Request(fqn, version, args, headers, request.mode); + } + + #processFqn(fqn: string): string + { + if (fqn.length === 0) + { + throw new BadRequest('Missing procedure name'); + } + + if (fqn.includes('..')) + { + throw new BadRequest('Invalid procedure name'); + } + + return fqn; + } + + #parseVersion(version?: string): Version + { + if (typeof version !== 'string') + { + return Version.DEFAULT; + } + + return this.#versionParser.parse(version); + } + + #mapArguments(args: Record): Map + { + return new Map(Object.entries(args)); + } + + #mapHeaders(headers: Record): Map + { + const map = new Map(); + + for (const [key, value] of Object.entries(headers)) + { + if (value === undefined) continue; + + const lowerKey = key.toLowerCase(); + const stringValue = value.toString(); + + map.set(lowerKey, stringValue); + } + + return map; + } + + #respondHealth(health: Map): ServerResponse + { + const result = Object.fromEntries(health); + const contentType = ContentTypes.JSON; + const headers = {}; + const status = StatusCodes.OK; + + return { result, contentType, headers, status }; + } + + #respondHealthy(healthy: boolean): ServerResponse + { + const result = healthy; + const contentType = ContentTypes.BOOLEAN; + const headers = {}; + const status = StatusCodes.OK; + + return { result, contentType, headers, status }; + } + + #respondFile(file: File): ServerResponse + { + const result = file.content; + const contentType = file.type; + const headers = {}; + const status = StatusCodes.OK; + + return { result, contentType, headers, status }; + } + + #respondResponse(response: Response): ServerResponse + { + const result = response.result instanceof Error ? response.result.message : response.result; + const contentType = this.#determineContentType(result); + const headers = this.#unmapHeaders(response.headers); + const status = response.status; + + return { result, contentType, headers, status }; + } + + #respondError(error: unknown): ServerResponse + { + const result = error instanceof Error ? error.message : String(error); + const contentType = this.#determineContentType(result); + const headers = {}; + const status = this.#determineStatusCode(error); + + return { result, contentType, headers, status }; + } + + #respondSuccess(): ServerResponse + { + const result = undefined; + const contentType = ContentTypes.TEXT; + const headers = {}; + const status = StatusCodes.OK; + + return { result, contentType, headers, status }; + } + + #determineContentType(content: unknown): string + { + if (content === undefined) return ContentTypes.UNDEFINED; + if (content === null) return ContentTypes.NULL; + + switch (typeof content) + { + case 'boolean': return ContentTypes.BOOLEAN; + case 'number': return ContentTypes.NUMBER; + case 'object': return ContentTypes.JSON; + default: return ContentTypes.TEXT; + } + } + + #unmapHeaders(headers: Map): Record + { + return Object.fromEntries(headers); + } + + #determineStatusCode(error: unknown): number + { + if (error instanceof BadRequest) return StatusCodes.BAD_REQUEST; + if (error instanceof Forbidden) return StatusCodes.FORBIDDEN; + if (error instanceof NotFound) return StatusCodes.NOT_FOUND; + if (error instanceof NotImplemented) return StatusCodes.NOT_IMPLEMENTED; + if (error instanceof PaymentRequired) return StatusCodes.PAYMENT_REQUIRED; + if (error instanceof Teapot) return StatusCodes.TEAPOT; + if (error instanceof Unauthorized) return StatusCodes.UNAUTHORIZED; + + return StatusCodes.SERVER_ERROR; + } + + #buildRemoteWorker(url: string, procedures: string[]): RemoteWorker + { + const remote = this.#remoteBuilder.build(url); + const procedureNames = new Set(procedures); + + return new RemoteWorker({ url, remote, procedureNames }); + } +} diff --git a/packages/runtime/src/server/ServerBuilder.ts b/packages/runtime/src/server/ServerBuilder.ts new file mode 100644 index 00000000..0495ee0c --- /dev/null +++ b/packages/runtime/src/server/ServerBuilder.ts @@ -0,0 +1,193 @@ + +import { ServerConfiguration, GatewayConfiguration, WorkerConfiguration, RepositoryConfiguration, ProxyConfiguration, StandaloneConfiguration } from '@jitar/configuration'; +import { Segment, ExecutionManager } from '@jitar/execution'; +import { HealthCheck, HealthManager } from '@jitar/health'; +import { Middleware, MiddlewareManager } from '@jitar/middleware'; +import { RemoteRepository, LocalRepository, RemoteGateway, LocalGateway, LocalWorker, Proxy, DummyProvider, DummyRunner, RemoteBuilder } from '@jitar/services'; +import { SourcingManager } from '@jitar/sourcing'; + +import UnknownServiceConfigured from './errors/UnknownServiceConfigured'; +import Server from './Server'; + +export default class RuntimeBuilder +{ + #sourcingManager: SourcingManager; + #remoteBuilder: RemoteBuilder; + + constructor(sourcingManager: SourcingManager, remoteBuilder: RemoteBuilder) + { + this.#sourcingManager = sourcingManager; + this.#remoteBuilder = remoteBuilder; + } + + async build(configuration: ServerConfiguration): Promise + { + const setUp = configuration.setUp ?? []; + const tearDown = configuration.tearDown ?? []; + const middleware = configuration.middleware; + const healthChecks = configuration.healthChecks; + + const proxy = await this.#buildService(configuration); + const sourcingManager = this.#sourcingManager; + const remoteBuilder = this.#remoteBuilder; + const middlewareManager = await this.#buildMiddlewareManager(middleware); + const healthManager = await this.#buildHealthManager(healthChecks); + const setUpScripts = setUp.map(filename => this.#makeSharedFilename(filename)); + const tearDownScripts = tearDown.map(filename => this.#makeSharedFilename(filename)); + + return new Server({ proxy, sourcingManager, remoteBuilder, middlewareManager, healthManager, setUpScripts, tearDownScripts }); + } + + #buildService(configuration: ServerConfiguration): Promise + { + if (configuration.gateway !== undefined) return this.#buildGatewayProxy(configuration.url, configuration.gateway); + if (configuration.worker !== undefined) return this.#buildWorkerProxy(configuration.url, configuration.worker); + if (configuration.repository !== undefined) return this.#buildRepositoryProxy(configuration.url, configuration.repository); + if (configuration.proxy !== undefined) return this.#buildProxy(configuration.url, configuration.proxy); + if (configuration.standalone !== undefined) return this.#buildStandalone(configuration.url, configuration.standalone); + + throw new UnknownServiceConfigured(); + } + + async #buildGatewayProxy(url: string, configuration: GatewayConfiguration): Promise + { + const provider = new DummyProvider(); + const runner = await this.#buildLocalGateway(url, configuration); + + return new Proxy({ url, provider, runner }); + } + + async #buildWorkerProxy(url: string, configuration: WorkerConfiguration): Promise + { + const provider = new DummyProvider(); + const runner = await this.#buildLocalWorker(url, configuration); + + return new Proxy({ url, provider, runner }); + } + + async #buildRepositoryProxy(url: string, configuration: RepositoryConfiguration): Promise + { + const provider = await this.#buildLocalRepository(url, configuration); + const runner = new DummyRunner(); + + return new Proxy({ url, provider, runner }); + } + + async #buildLocalGateway(url: string, configuration: GatewayConfiguration): Promise + { + const trustKey = configuration.trustKey; + const monitorInterval = configuration.monitor; + + return new LocalGateway({ url, trustKey, monitorInterval }); + } + + #buildRemoteGateway(url: string): RemoteGateway + { + const remote = this.#remoteBuilder.build(url); + + return new RemoteGateway({ url, remote }); + } + + async #buildLocalWorker(url: string, configuration: WorkerConfiguration): Promise + { + const trustKey = configuration.trustKey; + const gateway = configuration.gateway ? this.#buildRemoteGateway(configuration.gateway) : undefined; + const executionManager = await this.#buildExecutionManager(configuration.segments); + + return new LocalWorker({ url, trustKey, gateway, executionManager }); + } + + async #buildLocalRepository(url: string, configuration: RepositoryConfiguration): Promise + { + const sourcingManager = this.#sourcingManager; + const assets = await this.#buildAssetSet(configuration.assets); + const indexFilename = configuration.indexFilename; + const serveIndexOnNotFound = configuration.serveIndexOnNotFound; + + return new LocalRepository({ url, sourcingManager, assets, indexFilename, serveIndexOnNotFound }); + } + + #buildRemoteRepository(url: string): RemoteRepository + { + const remote = this.#remoteBuilder.build(url); + + return new RemoteRepository({ url, remote }); + } + + async #buildProxy(url: string, configuration: ProxyConfiguration): Promise + { + const provider = this.#buildRemoteRepository(configuration.repository); + const runner = this.#buildRemoteGateway(configuration.gateway); + + return new Proxy({ url, provider, runner }); + } + + async #buildStandalone(url: string, configuration: StandaloneConfiguration): Promise + { + const provider = await this.#buildLocalRepository(url, configuration); + const runner = await this.#buildLocalWorker(url, configuration); + + return new Proxy({ url, provider, runner }); + } + + async #buildHealthManager(filenames?: string[]): Promise + { + const manager = new HealthManager(); + + if (filenames !== undefined) + { + const sharedFilenames = filenames.map(filename => this.#makeSharedFilename(filename)); + const modules = await Promise.all(sharedFilenames.map(filename => this.#sourcingManager.import(filename))); + + modules.forEach(module => manager.addHealthCheck(module.default as HealthCheck)); + } + + return manager; + } + + async #buildMiddlewareManager(filenames?: string[]): Promise + { + const manager = new MiddlewareManager(); + + if (filenames !== undefined) + { + const sharedFilenames = filenames.map(filename => this.#makeSharedFilename(filename)); + const modules = await Promise.all(sharedFilenames.map(filename => this.#sourcingManager.import(filename))); + + modules.forEach(module => manager.addMiddleware(module.default as Middleware)); + } + + return manager; + } + + async #buildExecutionManager(segmentNames: string[]): Promise + { + const manager = new ExecutionManager(); + const filenames = segmentNames.map(name => `./${name}.segment.js`); + + const modules = await Promise.all(filenames.map(filename => this.#sourcingManager.import(filename))); + + modules.forEach(module => manager.addSegment(module.default as Segment)); + + return manager; + } + + async #buildAssetSet(patterns?: string[]): Promise> + { + if (patterns === undefined) return new Set(); + + const filenames = await this.#sourcingManager.filter(...patterns); + + return new Set(filenames); + } + + #makeSharedFilename(filename: string): string + { + if (filename.endsWith('.js') === false) + { + filename += '.js'; + } + + return filename.replace('.js', '.shared.js'); + } +} diff --git a/packages/server-nodejs/src/definitions/ContentTypes.ts b/packages/runtime/src/server/definitions/ContentTypes.ts similarity index 55% rename from packages/server-nodejs/src/definitions/ContentTypes.ts rename to packages/runtime/src/server/definitions/ContentTypes.ts index 9ad01504..f10ae002 100644 --- a/packages/server-nodejs/src/definitions/ContentTypes.ts +++ b/packages/runtime/src/server/definitions/ContentTypes.ts @@ -1,12 +1,12 @@ -const ContentTypes = { +const ContentTypes = +{ + NULL: 'application/null', + UNDEFINED: 'application/undefined', BOOLEAN: 'application/boolean', NUMBER: 'application/number', JSON: 'application/json', - TEXT: 'text/plain', - HTML: 'text/html', + TEXT: 'text/plain' } as const; -Object.freeze(ContentTypes); - export default ContentTypes; diff --git a/packages/runtime/src/server/definitions/StatusCodes.ts b/packages/runtime/src/server/definitions/StatusCodes.ts new file mode 100644 index 00000000..49bb5fdb --- /dev/null +++ b/packages/runtime/src/server/definitions/StatusCodes.ts @@ -0,0 +1,15 @@ + +const StatusCodes = +{ + OK: 200, + BAD_REQUEST: 400, + UNAUTHORIZED: 401, + PAYMENT_REQUIRED: 402, + FORBIDDEN: 403, + NOT_FOUND: 404, + TEAPOT: 418, + SERVER_ERROR: 500, + NOT_IMPLEMENTED: 501 +} as const; + +export default StatusCodes; diff --git a/packages/runtime/src/server/errors/UnknownServiceConfigured.ts b/packages/runtime/src/server/errors/UnknownServiceConfigured.ts new file mode 100644 index 00000000..7808c8b7 --- /dev/null +++ b/packages/runtime/src/server/errors/UnknownServiceConfigured.ts @@ -0,0 +1,8 @@ + +export default class UnknownServiceConfigured extends Error +{ + constructor() + { + super('Unknown service configured'); + } +} diff --git a/packages/runtime/src/server/types/AddWorkerRequest.ts b/packages/runtime/src/server/types/AddWorkerRequest.ts new file mode 100644 index 00000000..f9160f32 --- /dev/null +++ b/packages/runtime/src/server/types/AddWorkerRequest.ts @@ -0,0 +1,9 @@ + +type AddWorkerRequest = +{ + url: string; + procedureNames: string[]; + trustKey?: string; +}; + +export default AddWorkerRequest; diff --git a/packages/runtime/src/server/types/ProvideRequest.ts b/packages/runtime/src/server/types/ProvideRequest.ts new file mode 100644 index 00000000..660ffe1f --- /dev/null +++ b/packages/runtime/src/server/types/ProvideRequest.ts @@ -0,0 +1,7 @@ + +type ProvideRequest = +{ + filename: string; +}; + +export default ProvideRequest; diff --git a/packages/runtime/src/server/types/RunRequest.ts b/packages/runtime/src/server/types/RunRequest.ts new file mode 100644 index 00000000..abddbc94 --- /dev/null +++ b/packages/runtime/src/server/types/RunRequest.ts @@ -0,0 +1,13 @@ + +import { RunMode } from '@jitar/execution'; + +type RunRequest = +{ + fqn: string; + version?: string; + args: Record; + headers: Record; + mode: RunMode; +}; + +export default RunRequest; diff --git a/packages/runtime/src/server/types/ServerResponse.ts b/packages/runtime/src/server/types/ServerResponse.ts new file mode 100644 index 00000000..270b02a8 --- /dev/null +++ b/packages/runtime/src/server/types/ServerResponse.ts @@ -0,0 +1,10 @@ + +type ServerResponse = +{ + result: unknown; + contentType: string; + headers: Record; + status: number; +}; + +export default ServerResponse; diff --git a/packages/runtime/src/services/DummyRepository.ts b/packages/runtime/src/services/DummyRepository.ts deleted file mode 100644 index 54adc08a..00000000 --- a/packages/runtime/src/services/DummyRepository.ts +++ /dev/null @@ -1,38 +0,0 @@ - -import RepositoryNotAvailable from '../errors/RepositoryNotAvailable.js'; - -import File from '../models/File.js'; -import Module from '../types/Module.js'; - -import Repository from './Repository.js'; - -export default class DummyRepository extends Repository -{ - async start(): Promise { } - - async stop(): Promise { } - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - async registerClient(segmentFiles: string[]): Promise - { - throw new RepositoryNotAvailable(); - } - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - async readAsset(filename: string): Promise - { - throw new RepositoryNotAvailable(); - } - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - async readModule(filename: string, clientId: string): Promise - { - throw new RepositoryNotAvailable(); - } - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - async loadModule(filename: string): Promise - { - throw new RepositoryNotAvailable(); - } -} diff --git a/packages/runtime/src/services/Gateway.ts b/packages/runtime/src/services/Gateway.ts deleted file mode 100644 index fcb1c372..00000000 --- a/packages/runtime/src/services/Gateway.ts +++ /dev/null @@ -1,8 +0,0 @@ - -import Worker from './Worker.js'; -import ProcedureRuntime from './ProcedureRuntime.js'; - -export default abstract class Gateway extends ProcedureRuntime -{ - abstract addWorker(worker: Worker): Promise; -} diff --git a/packages/runtime/src/services/LocalRepository.ts b/packages/runtime/src/services/LocalRepository.ts deleted file mode 100644 index 8b8320b7..00000000 --- a/packages/runtime/src/services/LocalRepository.ts +++ /dev/null @@ -1,237 +0,0 @@ - -import { createRepositoryFilename, convertToLocalFilename, convertToRemoteFilename, isSegmentFilename } from '../definitions/Files.js'; - -import ClientNotFound from '../errors/ClientNotFound.js'; -import FileNotFound from '../errors/FileNotFound.js'; -import InvalidClientId from '../errors/InvalidClientId.js'; -import InvalidSegmentFile from '../errors/InvalidSegmentFile.js'; - -import FileManager from '../interfaces/FileManager.js'; -import File from '../models/File.js'; -import Module from '../types/Module.js'; -import ModuleLoader from '../utils/ModuleLoader.js'; -import ClientIdHelper from '../utils/ClientIdHelper.js'; - -import Repository from './Repository.js'; - -const clientIdHelper = new ClientIdHelper(); - -export default class LocalRepository extends Repository -{ - // All filenames used here are relative to the root location. - - #fileManager: FileManager; - #segments: Map = new Map(); - #clients: Map = new Map(); - - #segmentNames: Set = new Set(); - #assets: Set = new Set(); - #overrides: Map = new Map(); - - constructor(fileManager: FileManager, url?: string) - { - super(url); - - this.#fileManager = fileManager; - - ModuleLoader.setBaseUrl(fileManager.getRootLocation()); - } - - set segmentNames(names: Set) - { - this.#segmentNames = new Set(names); - } - - set assets(patterns: Set) - { - this.#assets = patterns; - } - - set overrides(overrides: Map) - { - this.#overrides = overrides; - } - - async start(): Promise - { - await super.start(); - - await this.#loadSegments(); - - this.#translateOverrides(); - } - - stop(): Promise - { - this.#unregisterClients(); - this.#unloadSegments(); - - return super.stop(); - } - - async #loadSegments(): Promise - { - for (const name of this.#segmentNames) - { - await this.#loadSegment(name); - } - } - - async #loadSegment(name: string): Promise - { - const relativeFilename = `./${createRepositoryFilename(name)}`; - const absoluteFilename = this.#fileManager.getAbsoluteLocation(relativeFilename); - const module = await ModuleLoader.load(absoluteFilename); - const files = module.files as string[]; - - if (files === undefined) - { - throw new InvalidSegmentFile(absoluteFilename); - } - - this.registerSegment(name, files); - } - - #unloadSegments(): void - { - this.#segments.clear(); - } - - async registerSegment(name: string, filenames: string[]): Promise - { - filenames.forEach((filename: string) => this.#segments.set(filename, name)); - } - - #translateOverrides(): void - { - const translated = new Map(); - - for (const [targetName, destinationName] of this.#overrides) - { - const relativeTargetFilename = ModuleLoader.assureExtension(targetName); - const relativeDestinationFilename = ModuleLoader.assureExtension(destinationName); - - const absoluteTargetFilename = this.#fileManager.getAbsoluteLocation(relativeTargetFilename); - const absoluteDestinationFilename = this.#fileManager.getAbsoluteLocation(relativeDestinationFilename); - - translated.set(absoluteTargetFilename, absoluteDestinationFilename); - } - - this.#overrides = translated; - } - - async registerClient(segmentFilenames: string[]): Promise - { - const clientId = clientIdHelper.generate(); - - this.#clients.set(clientId, segmentFilenames); - - return clientId; - } - - #unregisterClients(): void - { - this.#clients.clear(); - } - - readAsset(filename: string): Promise - { - if (this.#assets.has(filename) === false) - { - throw new FileNotFound(filename); - } - - return this.#readFile(filename); - } - - readModule(name: string, clientId: string): Promise - { - clientId = this.#validateClientId(clientId); - - const segmentFilename = this.#segments.get(name); - - if (segmentFilename === undefined) - { - return this.#readWorkerModule(name); - } - - return this.#hasClientSegmentFile(clientId, segmentFilename) - ? this.#readWorkerModule(name) - : this.#readRemoteModule(name); - } - - loadModule(name: string): Promise - { - const filename = this.#getModuleFilename(name); - - return ModuleLoader.load(filename); - } - - #validateClientId(clientId: string): string - { - if (clientIdHelper.validate(clientId) === false) - { - throw new InvalidClientId(clientId); - } - - if (this.#clients.has(clientId) === false) - { - throw new ClientNotFound(clientId); - } - - return clientId; - } - - #hasClientSegmentFile(clientId: string, segmentFilename: string): boolean - { - const clientSegmentFiles = this.#clients.get(clientId); - - if (clientSegmentFiles === undefined) - { - throw new ClientNotFound(clientId); - } - - return clientSegmentFiles.some(clientSegmentFilename => segmentFilename.endsWith(clientSegmentFilename)); - } - - async #readWorkerModule(name: string): Promise - { - const filename = this.#getModuleFilename(name); - const file = await this.#readFile(filename); - const code = file.content.toString(); - - return new File(filename, 'application/javascript', code); - } - - #readRemoteModule(name: string): Promise - { - const remoteFilename = convertToRemoteFilename(name); - - return this.#readFile(remoteFilename); - } - - #getModuleFilename(name: string): string - { - const relativeFilename = ModuleLoader.assureExtension(name); - const absoluteFilename = this.#fileManager.getAbsoluteLocation(relativeFilename); - - if (isSegmentFilename(absoluteFilename)) - { - return absoluteFilename; - } - - const assignedFilename = this.#getAssignedFilename(absoluteFilename); - - return convertToLocalFilename(assignedFilename); - } - - #getAssignedFilename(filename: string): string - { - return this.#overrides.get(filename) ?? filename; - } - - #readFile(filename: string): Promise - { - return this.#fileManager.read(filename); - } -} diff --git a/packages/runtime/src/services/LocalWorker.ts b/packages/runtime/src/services/LocalWorker.ts deleted file mode 100644 index e97e0f59..00000000 --- a/packages/runtime/src/services/LocalWorker.ts +++ /dev/null @@ -1,191 +0,0 @@ - -import { ExecutionScopes } from '../definitions/ExecutionScope.js'; -import { createWorkerFilename } from '../definitions/Files.js'; - -import Unauthorized from '../errors/generic/Unauthorized.js'; -import ImplementationNotFound from '../errors/ImplementationNotFound.js'; -import ProcedureNotFound from '../errors/ProcedureNotFound.js'; -import InvalidTrustKey from '../errors/InvalidTrustKey.js'; - -import Procedure from '../models/Procedure.js'; -import Request from '../models/Request.js'; -import Response from '../models/Response.js'; -import Segment from '../models/Segment.js'; -import ArgumentConstructor from '../utils/ArgumentConstructor.js'; - -import Gateway from './Gateway.js'; -import Worker from './Worker.js'; -import Repository from './Repository.js'; - -import { setRuntime } from '../hooks.js'; - -const JITAR_TRUST_HEADER_KEY = 'X-Jitar-Trust-Key'; - -export default class LocalWorker extends Worker -{ - #gateway?: Gateway; - #trustKey?: string; - #argumentConstructor: ArgumentConstructor; - - #segmentNames: Set = new Set(); - #segments: Map = new Map(); - - constructor(repository: Repository, gateway?: Gateway, url?: string, trustKey?: string, argumentConstructor = new ArgumentConstructor()) - { - super(repository, url); - - this.#gateway = gateway; - this.#trustKey = trustKey; - this.#argumentConstructor = argumentConstructor; - - setRuntime(this); - } - - get trustKey() { return this.#trustKey; } - - set segmentNames(names: Set) - { - this.#segmentNames = names; - } - - async start(): Promise - { - await super.start(); - - await this.#loadSegments(); - - if (this.#gateway !== undefined) - { - await this.#gateway.start(); - } - } - - async stop(): Promise - { - this.#unloadSegments(); - - if (this.#gateway !== undefined) - { - await this.#gateway.stop(); - } - - await super.stop(); - } - - getProcedureNames(): string[] - { - const names: Set = new Set(); - - for (const segment of this.#segments.values()) - { - // We only expose the public procedures - // to protect access to private procedures - - const procedures = segment.getExposedProcedures(); - - procedures.forEach(procedure => names.add(procedure.fqn)); - } - - return [...names.values()]; - } - - addSegment(segment: Segment): void - { - this.#segments.set(segment.id, segment); - } - - hasProcedure(fqn: string): boolean - { - const procedureNames = this.getProcedureNames(); - - return procedureNames.includes(fqn); - } - - run(request: Request): Promise - { - const procedure = this.#getProcedure(request.fqn); - - return procedure === undefined - ? this.#useGateway(request) - : this.#runProcedure(procedure, request); - } - - async #loadSegments() - { - for (const segmentName of this.#segmentNames) - { - await this.#loadSegment(segmentName); - } - } - - async #loadSegment(name: string): Promise - { - const filename = createWorkerFilename(name); - const module = await this.import(filename, ExecutionScopes.APPLICATION); - const segment = module.segment as Segment; - - this.addSegment(segment); - } - - #unloadSegments(): void - { - this.#segments.clear(); - } - - #useGateway(request: Request): Promise - { - if (this.#gateway === undefined) - { - throw new ProcedureNotFound(request.fqn); - } - - if (this.#trustKey !== undefined) - { - const headers = request.headers; - headers.set(JITAR_TRUST_HEADER_KEY, this.#trustKey); - } - - return this.#gateway.run(request); - } - - async #runProcedure(procedure: Procedure, request: Request): Promise - { - const trustKey = request.getHeader(JITAR_TRUST_HEADER_KEY); - - if (trustKey !== undefined && this.#trustKey !== trustKey) - { - throw new InvalidTrustKey(); - } - - if (trustKey === undefined && procedure.protected) - { - throw new Unauthorized(); - } - - const implementation = procedure.getImplementation(request.version); - - if (implementation === undefined) - { - throw new ImplementationNotFound(procedure.fqn, request.version.toString()); - } - - const values: unknown[] = this.#argumentConstructor.extract(implementation.parameters, request.args); - - const result = await implementation.executable.call(request, ...values); - - return new Response(result); - } - - #getProcedure(fqn: string): Procedure | undefined - { - for (const segment of this.#segments.values()) - { - if (segment.hasProcedure(fqn)) - { - return segment.getProcedure(fqn); - } - } - - return undefined; - } -} diff --git a/packages/runtime/src/services/ProcedureRunner.ts b/packages/runtime/src/services/ProcedureRunner.ts deleted file mode 100644 index b84dc779..00000000 --- a/packages/runtime/src/services/ProcedureRunner.ts +++ /dev/null @@ -1,23 +0,0 @@ - -import Middleware from '../interfaces/Middleware.js'; -import Request from '../models/Request.js'; -import Response from '../models/Response.js'; -import NextHandler from '../types/NextHandler.js'; - -import ProcedureRuntime from './ProcedureRuntime.js'; - -export default class ProcedureRunner implements Middleware -{ - #runner: ProcedureRuntime; - - constructor(runner: ProcedureRuntime) - { - this.#runner = runner; - } - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - async handle(request: Request, next: NextHandler): Promise - { - return this.#runner.run(request); - } -} diff --git a/packages/runtime/src/services/ProcedureRuntime.ts b/packages/runtime/src/services/ProcedureRuntime.ts deleted file mode 100644 index f22e96c8..00000000 --- a/packages/runtime/src/services/ProcedureRuntime.ts +++ /dev/null @@ -1,131 +0,0 @@ - -import { ExecutionScope, ExecutionScopes } from '../definitions/ExecutionScope.js'; - -import InvalidMiddleware from '../errors/InvalidMiddleware.js'; - -import Middleware from '../interfaces/Middleware.js'; -import Runner from '../interfaces/Runner.js'; - -import Request from '../models/Request.js'; -import Response from '../models/Response.js'; - -import Module from '../types/Module.js'; -import NextHandler from '../types/NextHandler.js'; - -import ProcedureRunner from './ProcedureRunner.js'; -import Repository from './Repository.js'; -import Runtime from './Runtime.js'; - -export default abstract class ProcedureRuntime extends Runtime implements Runner -{ - #repository: Repository; - #middlewareFiles: Set = new Set(); - #middlewares: Middleware[] = []; - - constructor(repository: Repository, url?: string) - { - super(url); - - this.#repository = repository; - - this.#middlewares.push(new ProcedureRunner(this)); - } - - get repository() { return this.#repository; } - - set middlewareFiles(filenames: Set) - { - this.#middlewareFiles = filenames; - } - - async start(): Promise - { - await this.#repository.start(); - - await super.start(); - - await this.#importMiddlewares(); - } - - async stop(): Promise - { - this.#clearMiddlewares(); - - await this.#repository.stop(); - - await super.stop(); - } - - abstract getProcedureNames(): string[]; - - abstract hasProcedure(name: string): boolean; - - abstract run(request: Request): Promise; - - import(url: string, scope: ExecutionScope): Promise - { - return this.#repository.import(url, scope); - } - - addMiddleware(middleware: Middleware) - { - // We want to add the middleware before the ProcedureRunner because - // it is the last middleware that needs to be called. - - const index = this.#middlewares.length - 1; - - this.#middlewares.splice(index, 0, middleware); - } - - getMiddleware(type: Function): Middleware | undefined - { - return this.#middlewares.find(middleware => middleware instanceof type); - } - - handle(request: Request): Promise - { - const startHandler = this.#getNextHandler(request, 0); - - return startHandler(); - } - - #getNextHandler(request: Request, index: number): NextHandler - { - const next = this.#middlewares[index]; - - if (next === undefined) - { - return async () => new Response(); - } - - const nextHandler = this.#getNextHandler(request, index + 1); - - return async () => { return next.handle(request, nextHandler); }; - } - - async #importMiddlewares(): Promise - { - for (const url of this.#middlewareFiles) - { - await this.#importMiddleware(url); - } - } - - async #importMiddleware(url: string): Promise - { - const module = await this.import(url, ExecutionScopes.APPLICATION); - const middleware = module.default as Middleware; - - if (middleware?.handle === undefined) - { - throw new InvalidMiddleware(url); - } - - this.addMiddleware(middleware); - } - - #clearMiddlewares(): void - { - this.#middlewares = []; - } -} diff --git a/packages/runtime/src/services/Proxy.ts b/packages/runtime/src/services/Proxy.ts deleted file mode 100644 index 6bfdd513..00000000 --- a/packages/runtime/src/services/Proxy.ts +++ /dev/null @@ -1,69 +0,0 @@ - -import File from '../models/File.js'; -import Request from '../models/Request.js'; -import Response from '../models/Response.js'; - -import RemoteGateway from './RemoteGateway.js'; -import RemoteWorker from './RemoteWorker.js'; -import RemoteRepository from './RemoteRepository.js'; -import ProcedureRuntime from './ProcedureRuntime.js'; - -export default class Proxy extends ProcedureRuntime -{ - #runner: RemoteGateway | RemoteWorker; - - constructor(repository: RemoteRepository, runner: RemoteGateway | RemoteWorker, url?: string) - { - super(repository, url); - - this.#runner = runner; - } - - get runner() { return this.#runner; } - - async start(): Promise - { - await super.start(); - - await this.#runner.start(); - } - - async stop(): Promise - { - await this.#runner.stop(); - - await super.stop(); - } - - getProcedureNames(): string[] - { - return this.#runner.getProcedureNames(); - } - - hasProcedure(fqn: string): boolean - { - const procedureNames = this.getProcedureNames(); - - return procedureNames.includes(fqn); - } - - readAsset(filename: string): Promise - { - return this.repository.readAsset(filename); - } - - registerClient(segmentFiles: string[]): Promise - { - return this.repository.registerClient(segmentFiles); - } - - readModule(filename: string, clientId: string): Promise - { - return this.repository.readModule(filename, clientId); - } - - run(request: Request): Promise - { - return this.#runner.run(request); - } -} diff --git a/packages/runtime/src/services/Remote.ts b/packages/runtime/src/services/Remote.ts deleted file mode 100644 index 5c9b160a..00000000 --- a/packages/runtime/src/services/Remote.ts +++ /dev/null @@ -1,186 +0,0 @@ - -import { Serializer, SerializerBuilder } from '@jitar/serialization'; - -import File from '../models/File.js'; -import Request from '../models/Request.js'; -import { default as ResultResponse } from '../models/Response.js'; - -import RemoteClassLoader from '../utils/RemoteClassLoader.js'; - -import Worker from './Worker.js'; - -const remoteClassLoader = new RemoteClassLoader(); -const defaultSerializer = SerializerBuilder.build(remoteClassLoader); - -const APPLICATION_JSON = 'application/json'; - -export default class Remote -{ - #url: string; - #serializer: Serializer; - - constructor(url: string, serializer: Serializer = defaultSerializer) - { - this.#url = url; - this.#serializer = serializer; - } - - async registerClient(segmentFiles: string[]): Promise - { - const url = `${this.#url}/modules`; - const options = - { - method: 'POST', - headers: { 'Content-Type': APPLICATION_JSON }, - body: JSON.stringify(segmentFiles) - }; - - const response = await this.#callRemote(url, options); - - return response.text(); - } - - async loadFile(filename: string): Promise - { - const url = `${this.#url}/${filename}`; - const options = { method: 'GET' }; - - const response = await this.#callRemote(url, options); - const type = response.headers.get('Content-Type') || 'application/octet-stream'; - const content = await response.text(); - - return new File(filename, type, content); - } - - async isHealthy(): Promise - { - const url = `${this.#url}/health/status`; - const options = { method: 'GET' }; - - const response = await this.#callRemote(url, options); - const healthy = await response.text(); - - return Boolean(healthy); - } - - async getHealth(): Promise> - { - const url = `${this.#url}/health`; - const options = { method: 'GET' }; - - const response = await this.#callRemote(url, options); - const health = await response.json(); - - return new Map(Object.entries(health)); - } - - async addWorker(worker: Worker): Promise - { - const url = `${this.#url}/workers`; - const body = - { - url: worker.url, - procedureNames: worker.getProcedureNames(), - trustKey: worker.trustKey, - }; - const options = - { - method: 'POST', - headers: { 'Content-Type': APPLICATION_JSON }, - body: JSON.stringify(body) - }; - - await this.#callRemote(url, options); - } - - async run(request: Request): Promise - { - request.setHeader('content-type', APPLICATION_JSON); - - const versionString = request.version.toString(); - const argsObject = Object.fromEntries(request.args); - const headersObject = Object.fromEntries(request.headers); - - const url = `${this.#url}/rpc/${request.fqn}?version=${versionString}&serialize=true`; - const body = await this.#createRequestBody(argsObject); - const options = - { - method: 'POST', - headers: headersObject, - body: body - }; - - const response = await this.#callRemote(url, options); - const result = await this.#createResponseResult(response); - const headers = this.#createResponseHeaders(response); - - return new ResultResponse(result, headers); - } - - async #callRemote(url: string, options: object): Promise - { - const response = await fetch(url, options); - - if (this.#isErrorResponse(response)) - { - throw await this.#createResponseResult(response); - } - - return response; - } - - #isErrorResponse(response: Response): boolean - { - return response.status < 200 || response.status > 299; - } - - async #createRequestBody(body: unknown): Promise - { - const data = await this.#serializer.serialize(body); - - return JSON.stringify(data); - } - - async #createResponseResult(response: Response): Promise - { - const result = await this.#getResponseResult(response); - - return this.#serializer.deserialize(result); - } - - async #getResponseResult(response: Response): Promise - { - const contentType = response.headers.get('Content-Type'); - - if (contentType !== null && contentType.includes('json')) - { - return response.json(); - } - - const content = await response.text(); - - if (contentType !== null && contentType.includes('boolean')) - { - return content === 'true'; - } - - if (contentType !== null && contentType.includes('number')) - { - return Number(content); - } - - return content; - } - - #createResponseHeaders(response: Response): Map - { - const headers = new Map(); - - for (const [name, value] of response.headers) - { - headers.set(name, value); - } - - return headers; - } -} diff --git a/packages/runtime/src/services/RemoteGateway.ts b/packages/runtime/src/services/RemoteGateway.ts deleted file mode 100644 index 6fb8e137..00000000 --- a/packages/runtime/src/services/RemoteGateway.ts +++ /dev/null @@ -1,58 +0,0 @@ - -import NotImplemented from '../errors/generic/NotImplemented.js'; - -import Request from '../models/Request.js'; -import Response from '../models/Response.js'; - -import DummyRepository from './DummyRepository.js'; -import Gateway from './Gateway.js'; -import Worker from './Worker.js'; -import Remote from './Remote.js'; - -export default class RemoteGateway extends Gateway -{ - #remote: Remote; - #worker?: Worker; - - constructor(url: string) - { - super(new DummyRepository(), url); - - this.#remote = new Remote(url); - } - - get worker() { return this.#worker; } - - set worker(worker: Worker | undefined) { this.#worker = worker; } - - async start(): Promise - { - await super.start(); - - if (this.#worker !== undefined) - { - await this.addWorker(this.#worker); - } - } - - getProcedureNames(): string[] - { - throw new NotImplemented(); - } - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - hasProcedure(name: string): boolean - { - throw new NotImplemented(); - } - - addWorker(worker: Worker): Promise - { - return this.#remote.addWorker(worker); - } - - run(request: Request): Promise - { - return this.#remote.run(request); - } -} diff --git a/packages/runtime/src/services/RemoteRepository.ts b/packages/runtime/src/services/RemoteRepository.ts deleted file mode 100644 index 1f06f0e7..00000000 --- a/packages/runtime/src/services/RemoteRepository.ts +++ /dev/null @@ -1,59 +0,0 @@ - -import File from '../models/File.js'; -import Module from '../types/Module.js'; -import ModuleLoader from '../utils/ModuleLoader.js'; - -import Remote from './Remote.js'; -import Repository from './Repository.js'; - -export default class RemoteRepository extends Repository -{ - #remote: Remote; - #segmentNames: Set = new Set(); - - constructor(url: string) - { - super(url); - - this.#remote = new Remote(url); - } - - get segmentNames(): string[] { return [...this.#segmentNames.values()]; } - - set segmentNames(segmentNames: Set) { this.#segmentNames = segmentNames; } - - async start(): Promise - { - const clientId = await this.registerClient(this.segmentNames); - const baseUrl = this.#getModuleBaseUrl(clientId); - - ModuleLoader.setBaseUrl(baseUrl); - - await super.start(); - } - - registerClient(segmentFiles: string[]): Promise - { - return this.#remote.registerClient(segmentFiles); - } - - readAsset(filename: string): Promise - { - return this.#remote.loadFile(filename); - } - - readModule(filename: string, clientId: string): Promise - { - return this.#remote.loadFile(`modules/${clientId}/${filename}`); - } - - loadModule(filename: string): Promise - { - return ModuleLoader.load(filename); - } - - #getModuleBaseUrl(clientId: string): string - { - return `${this.url}/modules/${clientId}`; - } -} diff --git a/packages/runtime/src/services/RemoteWorker.ts b/packages/runtime/src/services/RemoteWorker.ts deleted file mode 100644 index cd754272..00000000 --- a/packages/runtime/src/services/RemoteWorker.ts +++ /dev/null @@ -1,52 +0,0 @@ - -import Request from '../models/Request.js'; -import Response from '../models/Response.js'; - -import DummyRepository from './DummyRepository.js'; -import Worker from './Worker.js'; -import Remote from './Remote.js'; - -export default class RemoteWorker extends Worker -{ - #remote: Remote; - #procedureNames: Set = new Set(); - - constructor(url: string) - { - super(new DummyRepository(), url); - - this.#remote = new Remote(url); - } - - get trustKey() { return undefined; } - - set procedureNames(names: Set) - { - this.#procedureNames = names; - } - - getProcedureNames(): string[] - { - return [...this.#procedureNames.values()]; - } - - hasProcedure(name: string): boolean - { - return this.#procedureNames.has(name); - } - - isHealthy(): Promise - { - return this.#remote.isHealthy(); - } - - getHealth(): Promise> - { - return this.#remote.getHealth(); - } - - run(request: Request): Promise - { - return this.#remote.run(request); - } -} diff --git a/packages/runtime/src/services/Repository.ts b/packages/runtime/src/services/Repository.ts deleted file mode 100644 index 9f9656ba..00000000 --- a/packages/runtime/src/services/Repository.ts +++ /dev/null @@ -1,29 +0,0 @@ - -import { ExecutionScope, ExecutionScopes } from '../definitions/ExecutionScope.js'; - -import File from '../models/File.js'; -import Module from '../types/Module.js'; -import ModuleLoader from '../utils/ModuleLoader.js'; - -import Runtime from './Runtime.js'; - -export default abstract class Repository extends Runtime -{ - async import(url: string, scope: ExecutionScope): Promise - { - if (scope === ExecutionScopes.RUNTIME) - { - return ModuleLoader.load(url); - } - - return this.loadModule(url); - } - - abstract registerClient(segmentFiles: string[]): Promise; - - abstract readAsset(filename: string): Promise; - - abstract readModule(name: string, clientId: string): Promise; - - abstract loadModule(name: string): Promise; -} diff --git a/packages/runtime/src/services/Standalone.ts b/packages/runtime/src/services/Standalone.ts deleted file mode 100644 index ce4caf9a..00000000 --- a/packages/runtime/src/services/Standalone.ts +++ /dev/null @@ -1,70 +0,0 @@ - -import File from '../models/File.js'; -import Request from '../models/Request.js'; -import Response from '../models/Response.js'; - -import LocalWorker from './LocalWorker.js'; -import LocalRepository from './LocalRepository.js'; -import ProcedureRuntime from './ProcedureRuntime.js'; - -export default class Standalone extends ProcedureRuntime -{ - #worker: LocalWorker; - - constructor(repository: LocalRepository, worker: LocalWorker, url?: string) - { - super(repository, url); - - this.#worker = worker; - } - - get worker() { return this.#worker; } - - async start(): Promise - { - // The worker will start the repository - await this.#worker.start(); - - await super.start(); - } - - async stop(): Promise - { - // The worker will stop the repository - await this.#worker.stop(); - - await super.stop(); - } - - getProcedureNames(): string[] - { - return this.#worker.getProcedureNames(); - } - - hasProcedure(fqn: string): boolean - { - const procedureNames = this.getProcedureNames(); - - return procedureNames.includes(fqn); - } - - readAsset(filename: string): Promise - { - return this.repository.readAsset(filename); - } - - registerClient(segmentFiles: string[]): Promise - { - return this.repository.registerClient(segmentFiles); - } - - readModule(filename: string, clientId: string): Promise - { - return this.repository.readModule(filename, clientId); - } - - run(request: Request): Promise - { - return this.#worker.run(request); - } -} diff --git a/packages/runtime/src/services/Worker.ts b/packages/runtime/src/services/Worker.ts deleted file mode 100644 index 00e62b62..00000000 --- a/packages/runtime/src/services/Worker.ts +++ /dev/null @@ -1,7 +0,0 @@ - -import ProcedureRuntime from './ProcedureRuntime.js'; - -export default abstract class Worker extends ProcedureRuntime -{ - abstract get trustKey(): string | undefined; -} diff --git a/packages/runtime/src/types/ModuleImporter.ts b/packages/runtime/src/types/ModuleImporter.ts deleted file mode 100644 index 6b7df5de..00000000 --- a/packages/runtime/src/types/ModuleImporter.ts +++ /dev/null @@ -1,6 +0,0 @@ - -import Module from './Module.js'; - -type ModuleImporter = (name: string) => Promise; - -export default ModuleImporter; diff --git a/packages/runtime/src/utils/ClientIdHelper.ts b/packages/runtime/src/utils/ClientIdHelper.ts deleted file mode 100644 index 6c8d03f8..00000000 --- a/packages/runtime/src/utils/ClientIdHelper.ts +++ /dev/null @@ -1,18 +0,0 @@ - -const CLIENT_ID_PREFIX = 'CLIENT_'; -const CLIENT_ID_REGEX = /^CLIENT_\d+$/; - -let lastClientId = 0; - -export default class ClientIdHelper -{ - generate(): string - { - return `${CLIENT_ID_PREFIX}${lastClientId++}`; - } - - validate(clientId: string): boolean - { - return CLIENT_ID_REGEX.test(clientId); - } -} diff --git a/packages/runtime/src/utils/Environment.ts b/packages/runtime/src/utils/Environment.ts deleted file mode 100644 index 01394110..00000000 --- a/packages/runtime/src/utils/Environment.ts +++ /dev/null @@ -1,15 +0,0 @@ - -const hasWindowObject = typeof window !== 'undefined'; - -export default class Environment -{ - static isBrowser(): boolean - { - return hasWindowObject; - } - - static isServer(): boolean - { - return hasWindowObject === false; - } -} diff --git a/packages/runtime/src/utils/ModuleLoader.ts b/packages/runtime/src/utils/ModuleLoader.ts deleted file mode 100644 index 7270fc7b..00000000 --- a/packages/runtime/src/utils/ModuleLoader.ts +++ /dev/null @@ -1,77 +0,0 @@ - -import ModuleNotAccessible from '../errors/ModuleNotAccessible.js'; -import ModuleNotLoaded from '../errors/ModuleNotLoaded.js'; - -import Module from '../types/Module.js'; -import ModuleImporter from '../types/ModuleImporter.js'; -import Environment from './Environment.js'; - -import UrlRewriter from './UrlRewriter.js'; - -const APPLICATION_MODULE_INDICATORS = ['.', '/', 'http:', 'https:']; - -let _baseUrl: string = ''; -let _import = async (name: string): Promise => { return import(name); }; - -export default class ModuleLoader -{ - static setBaseUrl(baseUrl: string): void - { - _baseUrl = baseUrl; - } - - static setImporter(importer: ModuleImporter): void - { - _import = importer; - } - - static async load(specifier: string): Promise - { - if (this.#isSystemModule(specifier)) - { - return this.#import(specifier); - } - - if (specifier.startsWith('/jitar')) - { - return Environment.isServer() - ? this.#import('JITAR_LIBRARY_NAME') - : this.#import(specifier); - } - - const filename = this.assureExtension(specifier); - const url = UrlRewriter.addBase(filename, _baseUrl); - - if (url.startsWith(_baseUrl) === false) - { - throw new ModuleNotAccessible(specifier); - } - - return this.#import(url); - } - - static assureExtension(specifier: string): string - { - return specifier.endsWith('.js') ? specifier : `${specifier}.js`; - } - - static #isSystemModule(specifier: string): boolean - { - return APPLICATION_MODULE_INDICATORS.some((indicator: string) => specifier.startsWith(indicator)) === false; - } - - static async #import(specifier: string): Promise - { - try - { - return await _import(specifier); - } - catch (error: unknown) - { - const safeUrl = UrlRewriter.removeBase(specifier, _baseUrl); - const message = error instanceof Error ? error.message : String(error); - - throw new ModuleNotLoaded(safeUrl, message); - } - } -} diff --git a/packages/runtime/src/utils/RemoteClassLoader.ts b/packages/runtime/src/utils/RemoteClassLoader.ts deleted file mode 100644 index 0c3aecc9..00000000 --- a/packages/runtime/src/utils/RemoteClassLoader.ts +++ /dev/null @@ -1,29 +0,0 @@ - -import { ClassLoader, Loadable, ClassNotFound, InvalidClass } from '@jitar/serialization'; - -import ModuleLoader from './ModuleLoader.js'; - -export default class RemoteClassLoader implements ClassLoader -{ - async loadClass(loadable: Loadable): Promise - { - if (typeof loadable.source !== 'string') - { - throw new ClassNotFound(loadable.name); - } - - const module = await ModuleLoader.load(loadable.source); - const clazz = (module[loadable.name] ?? module['default']) as Function; - - if (clazz === undefined) - { - throw new ClassNotFound(loadable.name); - } - else if ((clazz instanceof Function) === false) - { - throw new InvalidClass(loadable.name); - } - - return clazz; - } -} diff --git a/packages/runtime/src/utils/UrlRewriter.ts b/packages/runtime/src/utils/UrlRewriter.ts deleted file mode 100644 index 43deccdb..00000000 --- a/packages/runtime/src/utils/UrlRewriter.ts +++ /dev/null @@ -1,55 +0,0 @@ - -export default class UrlRewriter -{ - static addBase(url: string, base: string): string - { - if (url.startsWith(base)) - { - return url; - } - - const joined = `${base}/${url}`; - const parts = joined.split('://'); - - const protocol = parts.length > 1 ? `${parts[0]}://` : ''; - const address = parts.length > 1 ? parts[1] : parts[0]; - - const translatedAddress = this.#translateAddress(address); - - return `${protocol}${translatedAddress}`; - } - - static removeBase(url: string, base: string): string - { - if (url.startsWith(base) === false) - { - return url; - } - - return url.substring(base.length); - } - - static #translateAddress(address: string) - { - const parts = address.split('/'); - const translated = []; - - translated.push(parts[0]); - - for (let index = 1; index < parts.length; index++) - { - const part = parts[index].trim(); - - switch (part) - { - case '': continue; - case '.': continue; - case '..': translated.pop(); continue; - } - - translated.push(part); - } - - return translated.join('/'); - } -} diff --git a/packages/runtime/test/_fixtures/interfaces/FileManager.fixture.ts b/packages/runtime/test/_fixtures/interfaces/FileManager.fixture.ts deleted file mode 100644 index 2c2f9914..00000000 --- a/packages/runtime/test/_fixtures/interfaces/FileManager.fixture.ts +++ /dev/null @@ -1,75 +0,0 @@ - -import FileManager from '../../../src/interfaces/FileManager'; -import File from '../../../src/models/File'; - -class TestFileManager implements FileManager -{ - getRootLocation(): string - { - return ''; - } - - getAbsoluteLocation(filename: string): string - { - return filename; - } - - getRelativeLocation(filename: string): string - { - return filename; - } - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - async getType(filename: string): Promise - { - return 'file'; - } - - async getContent(filename: string): Promise - { - switch (filename) - { - case 'private.local.js': - return Buffer.from('private()'); - - case 'first.local.js': - return Buffer.from('first()'); - - case 'fourth.remote.js': - return Buffer.from('fourth()'); - - case 'index.html': - return Buffer.from('

Hello world

'); - } - - return Buffer.from(''); - } - - async read(filename: string): Promise - { - const type = await this.getType(filename); - const content = await this.getContent(filename); - - return new File(filename, type, content); - } - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - async write(filename: string, content: string): Promise - { - // Do nothing - } - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - async delete(filename: string): Promise - { - // Do nothing - } - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - async filter(pattern: string): Promise - { - return []; - } -} - -export { TestFileManager }; diff --git a/packages/runtime/test/_fixtures/models/Executable.fixture.ts b/packages/runtime/test/_fixtures/models/Executable.fixture.ts deleted file mode 100644 index b33dd7a9..00000000 --- a/packages/runtime/test/_fixtures/models/Executable.fixture.ts +++ /dev/null @@ -1,30 +0,0 @@ - -import { runProcedure } from '../../../src/hooks'; - -const EXECUTABLES = -{ - // General - PRIVATE: () => { return 'private'; }, - PROTECTED: () => { return 'protected'; }, - PUBLIC: () => { return 'public'; }, - PARAMETERS: (mandatory: string, optional = 'default') => { return `${mandatory} ${optional}`; }, - BROKEN: () => { throw new Error('broken'); }, - CONTEXT: () => { return this; }, - V1_0_0: () => { return '1.0.0'; }, - V1_0_5: () => { return '1.0.5'; }, - V1_1_0: () => { return '1.1.0'; }, - - // First segment - FIRST: () => { return 'first'; }, - SECOND: () => { return runProcedure('first', '0.0.0', new Map()); }, // Runs a private task on the same segment - THIRD: () => { return runProcedure('fourth', '0.0.0', new Map()); }, // Runs a public task on another segment - - // Second segment - FOURTH: () => { return 'fourth'; }, - FIFTH: () => { return 'fifth'; }, - SIXTH: () => { return runProcedure('first', '0.0.0', new Map()); }, // Runs a private task on another segment -}; - -Object.freeze(EXECUTABLES); - -export { EXECUTABLES }; diff --git a/packages/runtime/test/_fixtures/models/Segment.fixture.ts b/packages/runtime/test/_fixtures/models/Segment.fixture.ts deleted file mode 100644 index c609126f..00000000 --- a/packages/runtime/test/_fixtures/models/Segment.fixture.ts +++ /dev/null @@ -1,37 +0,0 @@ - -import Segment from '../../../src/models/Segment'; - -import { PROCEDURES } from './Procedure.fixture'; - -const SEGMENTS = -{ - GENERAL: new Segment('general') - .addProcedure(PROCEDURES.PRIVATE) - .addProcedure(PROCEDURES.PROTECTED) - .addProcedure(PROCEDURES.PUBLIC) - .addProcedure(PROCEDURES.PARAMETERS) - .addProcedure(PROCEDURES.BROKEN) - .addProcedure(PROCEDURES.CONTEXT) - .addProcedure(PROCEDURES.VERSIONED), - - FIRST: new Segment('first') - .addProcedure(PROCEDURES.FIRST) - .addProcedure(PROCEDURES.SECOND) - .addProcedure(PROCEDURES.THIRD), - - SECOND: new Segment('second') - .addProcedure(PROCEDURES.FOURTH) - .addProcedure(PROCEDURES.FIFTH) - .addProcedure(PROCEDURES.SIXTH) -}; - -Object.freeze(SEGMENTS); - -const SEGMENT_FILES = -{ - GENERAL: ['private.js', 'public.js', 'parameters.js', 'broken.js', 'context.js', 'versioned.js'], - FIRST: ['first.js', 'second.js', 'third.js'], - SECOND: ['fourth.js', 'fifth.js', 'sixth.js'] -}; - -export { SEGMENTS, SEGMENT_FILES }; diff --git a/packages/runtime/test/_fixtures/services/LocalGateway.fixture.ts b/packages/runtime/test/_fixtures/services/LocalGateway.fixture.ts deleted file mode 100644 index 134071a5..00000000 --- a/packages/runtime/test/_fixtures/services/LocalGateway.fixture.ts +++ /dev/null @@ -1,32 +0,0 @@ - -import LocalGateway from '../../../src/services/LocalGateway'; - -import { REPOSITORIES } from './LocalRepository.fixture'; -import { WORKERS } from './LocalWorker.fixture'; - -const GATEWAY_URL = 'http://localhost:80'; - -const standaloneGateway = new LocalGateway(REPOSITORIES.DUMMY, GATEWAY_URL); -standaloneGateway.addWorker(WORKERS.SINGLE); - -const distributedGateway = new LocalGateway(REPOSITORIES.DUMMY, GATEWAY_URL); -distributedGateway.addWorker(WORKERS.FIRST); -distributedGateway.addWorker(WORKERS.SECOND); - -const healthGateway = new LocalGateway(REPOSITORIES.DUMMY, GATEWAY_URL); -healthGateway.addWorker(WORKERS.GOOD); -healthGateway.addWorker(WORKERS.BAD); - -const protectedGateway = new LocalGateway(REPOSITORIES.DUMMY, GATEWAY_URL, 'MY_PROTECTED_ACCESS_KEY'); -protectedGateway.addWorker(WORKERS.FIRST, 'MY_PROTECTED_ACCESS_KEY'); -protectedGateway.addWorker(WORKERS.SECOND); - -const GATEWAYS = -{ - STANDALONE: standaloneGateway, - DISTRIBUTED: distributedGateway, - HEALTH: healthGateway, - PROTECTED: protectedGateway -}; - -export { GATEWAYS, GATEWAY_URL }; diff --git a/packages/runtime/test/_fixtures/services/LocalRepository.fixture.ts b/packages/runtime/test/_fixtures/services/LocalRepository.fixture.ts deleted file mode 100644 index c50a5f97..00000000 --- a/packages/runtime/test/_fixtures/services/LocalRepository.fixture.ts +++ /dev/null @@ -1,36 +0,0 @@ - -import DummyRepository from '../../../src/services/DummyRepository'; -import LocalRepository from '../../../src/services/LocalRepository'; - -import { TestFileManager } from '../interfaces/FileManager.fixture'; -import { SEGMENT_FILES } from '../models/Segment.fixture'; - -const CLIENT = { id: '' }; - -const defaultRepository = new LocalRepository(new TestFileManager()); -defaultRepository.assets = new Set(['index.html']); - -await defaultRepository.registerSegment('first', SEGMENT_FILES.FIRST); -await defaultRepository.registerSegment('second', SEGMENT_FILES.SECOND); -await defaultRepository.registerClient(['first']).then(clientId => CLIENT.id = clientId); - -const dummyRepository = new DummyRepository(); - -const REPOSITORIES = -{ - DEFAULT: defaultRepository, - DUMMY: dummyRepository -}; - -Object.freeze(REPOSITORIES); - -const REPOSITORY_FILES = -{ - UNSEGMENTED: SEGMENT_FILES.GENERAL[0], - LOCAL: SEGMENT_FILES.FIRST[0], - REMOTE: SEGMENT_FILES.SECOND[0] -}; - -Object.freeze(REPOSITORY_FILES); - -export { REPOSITORIES, REPOSITORY_FILES, CLIENT }; diff --git a/packages/runtime/test/_fixtures/services/LocalWorker.fixture.ts b/packages/runtime/test/_fixtures/services/LocalWorker.fixture.ts deleted file mode 100644 index f922e162..00000000 --- a/packages/runtime/test/_fixtures/services/LocalWorker.fixture.ts +++ /dev/null @@ -1,39 +0,0 @@ - -import LocalWorker from '../../../src/services/LocalWorker'; -import { setRuntime } from '../../../src/hooks'; - -import { HEALTH_CHECKS } from '../interfaces/HealthCheck.fixture'; -import { SEGMENTS } from '../models/Segment.fixture'; -import { REPOSITORIES } from './LocalRepository.fixture'; - -const TRUST_KEY = 'MY_TRUST_KEY'; - -const singleWorker = new LocalWorker(REPOSITORIES.DUMMY, undefined, undefined, TRUST_KEY); -singleWorker.addSegment(SEGMENTS.GENERAL); -singleWorker.addSegment(SEGMENTS.FIRST); -singleWorker.addSegment(SEGMENTS.SECOND); - -const firstWorker = new LocalWorker(REPOSITORIES.DUMMY); -firstWorker.addSegment(SEGMENTS.FIRST); - -const secondWorker = new LocalWorker(REPOSITORIES.DUMMY); -secondWorker.addSegment(SEGMENTS.SECOND); - -const goodWorker = new LocalWorker(REPOSITORIES.DUMMY); -goodWorker.addHealthCheck(HEALTH_CHECKS.GOOD); - -const badWorker = new LocalWorker(REPOSITORIES.DUMMY); -badWorker.addHealthCheck(HEALTH_CHECKS.BAD); - -const WORKERS = -{ - SINGLE: singleWorker, - FIRST: firstWorker, - SECOND: secondWorker, - GOOD: goodWorker, - BAD: badWorker -}; - -setRuntime(singleWorker); - -export { WORKERS, TRUST_KEY }; diff --git a/packages/runtime/test/_fixtures/services/ProcedureRuntime.fixture.ts b/packages/runtime/test/_fixtures/services/ProcedureRuntime.fixture.ts deleted file mode 100644 index 6459c6d8..00000000 --- a/packages/runtime/test/_fixtures/services/ProcedureRuntime.fixture.ts +++ /dev/null @@ -1,35 +0,0 @@ - -import Request from '../../../src/models/Request'; -import Response from '../../../src/models/Response'; - -import ProcedureRuntime from '../../../src/services/ProcedureRuntime'; - -import { MIDDLEWARES } from '../interfaces/Middleware.fixture'; -import { REPOSITORIES } from './LocalRepository.fixture'; - -class MiddlewareRuntime extends ProcedureRuntime -{ - getProcedureNames(): string[] { return []; } - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - hasProcedure(name: string): boolean { return false; } - - async start() { } - - async stop() { } - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - async run(request: Request): Promise { return new Response(); } -} - -const middlewareRuntime = new MiddlewareRuntime(REPOSITORIES.DUMMY); -middlewareRuntime.addMiddleware(MIDDLEWARES.FIRST); -middlewareRuntime.addMiddleware(MIDDLEWARES.SECOND); -middlewareRuntime.addMiddleware(MIDDLEWARES.THIRD); - -const RUNTIMES = -{ - MIDDLEWARE: middlewareRuntime -}; - -export { RUNTIMES }; diff --git a/packages/runtime/test/_fixtures/services/Remote.fixture.ts b/packages/runtime/test/_fixtures/services/Remote.fixture.ts deleted file mode 100644 index 72a58b6c..00000000 --- a/packages/runtime/test/_fixtures/services/Remote.fixture.ts +++ /dev/null @@ -1,42 +0,0 @@ - -import Request from '../../../src/models/Request'; -import Version from '../../../src/models/Version'; -import Remote from '../../../src/services/Remote'; - -const remote = new Remote('http://localhost:3000'); -const CONTENT_TYPE = 'Content-Type'; - -const BOOLEAN_REQUEST = new Request('game/checkSecret', Version.DEFAULT, new Map(), new Map()); -const NUMBER_REQUEST = new Request('game/getSecret', Version.DEFAULT, new Map(), new Map()); -const OBJECT_REQUEST = new Request('game/scoreSecret', Version.DEFAULT, new Map(), new Map()); -const DEFAULT_REQUEST = new Request('game/guessSecret', Version.DEFAULT, new Map(), new Map()); - -const BOOLEAN_RESPONSE = new Response('false', { status: 200, statusText: 'OK', headers: { 'Content-Type': 'application/boolean' } }); -const NUMBER_RESPONSE = new Response('42', { status: 200, statusText: 'OK', headers: { 'Content-Type': 'application/number' } }); -const OBJECT_RESPONSE = new Response('{"result":42}', { status: 200, statusText: 'OK', headers: { 'Content-Type': 'application/json' } }); -const DEFAULT_RESPONSE = new Response('Sorry, try again', { status: 200, statusText: 'OK', headers: { 'Content-Type': 'text/plain' } }); - -const REQUESTS = { - BOOLEAN_REQUEST: BOOLEAN_REQUEST, - NUMBER_REQUEST: NUMBER_REQUEST, - OBJECT_REQUEST: OBJECT_REQUEST, - DEFAULT_REQUEST: DEFAULT_REQUEST -}; - -// eslint-disable-next-line @typescript-eslint/no-unused-vars -function customFetch(input: URL | RequestInfo, options: RequestInit | undefined): Promise -{ - const url = input.toString(); - - switch (url) - { - case 'http://localhost:3000/rpc/game/checkSecret?version=0.0.0&serialize=true': return Promise.resolve(BOOLEAN_RESPONSE); - case 'http://localhost:3000/rpc/game/getSecret?version=0.0.0&serialize=true': return Promise.resolve(NUMBER_RESPONSE); - case 'http://localhost:3000/rpc/game/scoreSecret?version=0.0.0&serialize=true': return Promise.resolve(OBJECT_RESPONSE); - default: return Promise.resolve(DEFAULT_RESPONSE); - } -} - -globalThis.fetch = customFetch; - -export { REQUESTS, remote, CONTENT_TYPE }; diff --git a/packages/runtime/test/_fixtures/services/RemoteWorker.fixture.ts b/packages/runtime/test/_fixtures/services/RemoteWorker.fixture.ts deleted file mode 100644 index 1fd453a1..00000000 --- a/packages/runtime/test/_fixtures/services/RemoteWorker.fixture.ts +++ /dev/null @@ -1,14 +0,0 @@ - -import RemoteWorker from '../../../src/services/RemoteWorker'; - -const WORKER_URL = 'http://localhost:80'; - -const remoteWorker = new RemoteWorker(WORKER_URL); -remoteWorker.procedureNames = new Set(['first', 'second']); - -const WORKERS = -{ - REMOTE: remoteWorker -}; - -export { WORKERS, WORKER_URL }; diff --git a/packages/runtime/test/_fixtures/services/Runtime.fixture.ts b/packages/runtime/test/_fixtures/services/Runtime.fixture.ts deleted file mode 100644 index 9a787ba9..00000000 --- a/packages/runtime/test/_fixtures/services/Runtime.fixture.ts +++ /dev/null @@ -1,45 +0,0 @@ - -import { ExecutionScope } from '../../../src/definitions/ExecutionScope'; -import Runtime from '../../../src/services/Runtime'; -import Module from '../../../src/types/Module'; - -import { HEALTH_CHECKS } from '../interfaces/HealthCheck.fixture'; - -class TestRuntime extends Runtime -{ - // eslint-disable-next-line @typescript-eslint/no-unused-vars - async import(url: string, scope: ExecutionScope): Promise - { - return {}; - } - - async start(): Promise { } - - async stop(): Promise { } -} - -const goodRuntime = new TestRuntime(); -goodRuntime.addHealthCheck(HEALTH_CHECKS.GOOD); - -const badRuntime = new TestRuntime(); -badRuntime.addHealthCheck(HEALTH_CHECKS.BAD); - -const errorRuntime = new TestRuntime(); -errorRuntime.addHealthCheck(HEALTH_CHECKS.ERROR); - -const timeoutRuntime = new TestRuntime(); -timeoutRuntime.addHealthCheck(HEALTH_CHECKS.TIMEDOUT); - -const inTimeRuntime = new TestRuntime(); -inTimeRuntime.addHealthCheck(HEALTH_CHECKS.INTIME); - -const RUNTIMES = -{ - GOOD: goodRuntime, - BAD: badRuntime, - ERROR: errorRuntime, - TIMEDOUT: timeoutRuntime, - INTIME: inTimeRuntime -}; - -export { RUNTIMES }; diff --git a/packages/runtime/test/_fixtures/services/WorkerBalancer.fixture.ts b/packages/runtime/test/_fixtures/services/WorkerBalancer.fixture.ts deleted file mode 100644 index fb12b1aa..00000000 --- a/packages/runtime/test/_fixtures/services/WorkerBalancer.fixture.ts +++ /dev/null @@ -1,23 +0,0 @@ - -import LocalWorker from '../../../src/services/LocalWorker'; -import WorkerBalancer from '../../../src/services/WorkerBalancer'; - -import { REPOSITORIES } from './LocalRepository.fixture'; - -const WORKERS = -{ - FIRST: new LocalWorker(REPOSITORIES.DUMMY), - SECOND: new LocalWorker(REPOSITORIES.DUMMY) -}; - -const filledBalancer = new WorkerBalancer(); -filledBalancer.addWorker(WORKERS.FIRST); -filledBalancer.addWorker(WORKERS.SECOND); - -const BALANCERS = -{ - FILLED: filledBalancer, - EMPTY: new WorkerBalancer() -}; - -export { BALANCERS, WORKERS }; diff --git a/packages/runtime/test/_fixtures/services/WorkerMonitor.fixture.ts b/packages/runtime/test/_fixtures/services/WorkerMonitor.fixture.ts deleted file mode 100644 index fb15ce67..00000000 --- a/packages/runtime/test/_fixtures/services/WorkerMonitor.fixture.ts +++ /dev/null @@ -1,14 +0,0 @@ - -import WorkerMonitor from '../../../src/services/WorkerMonitor'; - -import { WORKERS } from './LocalWorker.fixture'; -import { GATEWAYS } from './LocalGateway.fixture'; - -const GATEWAY = GATEWAYS.HEALTH; - -const MONITORS = -{ - HEALTH: new WorkerMonitor(GATEWAY, 100) -}; - -export { MONITORS, GATEWAY, WORKERS }; diff --git a/packages/runtime/test/_fixtures/utils/ModuleLoader.fixture.ts b/packages/runtime/test/_fixtures/utils/ModuleLoader.fixture.ts deleted file mode 100644 index da831f6f..00000000 --- a/packages/runtime/test/_fixtures/utils/ModuleLoader.fixture.ts +++ /dev/null @@ -1,13 +0,0 @@ - -const moduleImporter = async (specifier: string) => -{ - switch (specifier) - { - case '/root/app/public/app.js': return { default: function app() {} }; - case 'jitar': return { default: class Jitar {} }; - } - - throw Error('Not found'); -}; - -export { moduleImporter }; diff --git a/packages/runtime/test/dummy.spec.ts b/packages/runtime/test/dummy.spec.ts new file mode 100644 index 00000000..2489f1ee --- /dev/null +++ b/packages/runtime/test/dummy.spec.ts @@ -0,0 +1,12 @@ + +import { describe, expect, it } from 'vitest'; + +describe('dummy', () => +{ + // TODO: Add real tests + + it('should not complain about missing tests', async () => + { + expect(true).toBeTruthy(); + }); +}); diff --git a/packages/runtime/test/services/LocalGateway.spec.ts b/packages/runtime/test/services/LocalGateway.spec.ts deleted file mode 100644 index 59b96b2c..00000000 --- a/packages/runtime/test/services/LocalGateway.spec.ts +++ /dev/null @@ -1,108 +0,0 @@ - -import { describe, expect, it } from 'vitest'; - -import ProcedureNotFound from '../../src/errors/ProcedureNotFound'; -import InvalidTrustKey from '../../src/errors/InvalidTrustKey'; -import Request from '../../src/models/Request'; -import Version from '../../src/models/Version'; - -import { GATEWAYS, GATEWAY_URL } from '../_fixtures/services/LocalGateway.fixture'; - -const gateway = GATEWAYS.STANDALONE; - -describe('services/LocalGateway', () => -{ - describe('.url', () => - { - it('should contain an url', () => - { - expect(gateway.url).toContain(GATEWAY_URL); - }); - }); - - describe('.getProcedureNames()', () => - { - it('should contain all public and protected procedure names', () => - { - const procedureNames = gateway.getProcedureNames(); - - expect(procedureNames).toHaveLength(6); - expect(procedureNames).toContain('protected'); - expect(procedureNames).toContain('public'); - expect(procedureNames).toContain('second'); - expect(procedureNames).toContain('third'); - expect(procedureNames).toContain('fourth'); - expect(procedureNames).toContain('sixth'); - }); - }); - - describe('.hasProcedure(name)', () => - { - it('should have public procedures', () => - { - const hasProtectedProcedure = gateway.hasProcedure('protected'); - const hasPublicProcedure = gateway.hasProcedure('public'); - const hasSecondProcedure = gateway.hasProcedure('second'); - const hasThirdProcedure = gateway.hasProcedure('third'); - const hasFourthProcedure = gateway.hasProcedure('fourth'); - const hasSixthProcedure = gateway.hasProcedure('sixth'); - - expect(hasProtectedProcedure).toBeTruthy(); - expect(hasPublicProcedure).toBeTruthy(); - expect(hasSecondProcedure).toBeTruthy(); - expect(hasThirdProcedure).toBeTruthy(); - expect(hasFourthProcedure).toBeTruthy(); - expect(hasSixthProcedure).toBeTruthy(); - }); - }); - - describe('.run(name, version, parameters)', () => - { - it('should find and run a procedure from a worker', async () => - { - const request = new Request('second', Version.DEFAULT, new Map(), new Map()); - const response = await gateway.run(request); - - expect(response.result).toBe('first'); - }); - - it('should find and run a procedure from a worker that calls a procedure on another worker', async () => - { - const request = new Request('third', Version.DEFAULT, new Map(), new Map()); - const response = await gateway.run(request); - - expect(response.result).toBe('fourth'); - }); - - it('should not run a non-existing procedure', async () => - { - const request = new Request('nonExisting', Version.DEFAULT, new Map(), new Map()); - const run = async () => gateway.run(request); - - expect(run).rejects.toEqual(new ProcedureNotFound('nonExisting')); - }); - }); - - describe('.addWorker(worker, accessKey)', () => - { - it('should not add a worker with an incorrect access key', async () => - { - const worker = gateway.workers[0]; - const protectedGateway = GATEWAYS.PROTECTED; - - const addWorker = async () => protectedGateway.addWorker(worker, 'INCORRECT_ACCESS_KEY'); - - expect(addWorker).rejects.toEqual(new InvalidTrustKey()); - }); - - it('should not add a worker with an access key to an unprotected gateway', async () => - { - const worker = gateway.workers[0]; - const unprotectedGateway = GATEWAYS.STANDALONE; - - const addWorker = async () => unprotectedGateway.addWorker(worker, 'WORKER_ACCESS_KEY'); - - expect(addWorker).rejects.toEqual(new InvalidTrustKey()); - }); - }); -}); diff --git a/packages/runtime/test/services/LocalRepository.spec.ts b/packages/runtime/test/services/LocalRepository.spec.ts deleted file mode 100644 index 8d1b8929..00000000 --- a/packages/runtime/test/services/LocalRepository.spec.ts +++ /dev/null @@ -1,65 +0,0 @@ - -import { describe, expect, it } from 'vitest'; - -import InvalidClientId from '../../src/errors/InvalidClientId'; -import ClientNotFound from '../../src/errors/ClientNotFound'; - -import FileNotFound from '../../src/errors/FileNotFound'; -import { REPOSITORIES, REPOSITORY_FILES, CLIENT } from '../_fixtures/services/LocalRepository.fixture'; - -const repository = REPOSITORIES.DEFAULT; - -describe('services/LocalRepository', () => -{ - describe('.readModule(clientId, filename)', () => - { - it('should not accept an invalid client id', () => - { - const run = async () => repository.readModule('/some/file', 'INVALID'); - - expect(run).rejects.toEqual(new InvalidClientId('INVALID')); - }); - - it('should not accept an unknown client id', () => - { - const run = async () => repository.readModule('/some/file', 'CLIENT_9999'); - - expect(run).rejects.toEqual(new ClientNotFound('CLIENT_9999')); - }); - - it('should return an unsegmented module file', async () => - { - const result = await repository.readModule(REPOSITORY_FILES.UNSEGMENTED, CLIENT.id); - - expect(result.content.toString()).toContain('private()'); - }); - - it('should return the actual module file', async () => - { - const result = await repository.readModule(REPOSITORY_FILES.LOCAL, CLIENT.id); - - expect(result.content).toContain('first()'); - }); - - it('should return a remote module file', async () => - { - const result = await repository.readModule(REPOSITORY_FILES.REMOTE, CLIENT.id); - - expect(result.content.toString()).toContain('fourth()'); - }); - - it('should return a public asset', async () => - { - const result = await repository.readAsset('index.html'); - - expect(result.content.toString()).toContain('

Hello world

'); - }); - - it('should not return a private asset', async () => - { - const run = async () => repository.readAsset('style.css'); - - expect(run).rejects.toEqual(new FileNotFound('style.css')); - }); - }); -}); diff --git a/packages/runtime/test/services/LocalWorker.spec.ts b/packages/runtime/test/services/LocalWorker.spec.ts deleted file mode 100644 index 9c4513db..00000000 --- a/packages/runtime/test/services/LocalWorker.spec.ts +++ /dev/null @@ -1,121 +0,0 @@ - -import { describe, expect, it } from 'vitest'; - -import ProcedureNotFound from '../../src/errors/ProcedureNotFound'; -import Request from '../../src/models/Request'; -import Version from '../../src/models/Version'; - -import { WORKERS, TRUST_KEY } from '../_fixtures/services/LocalWorker.fixture'; -import Unauthorized from '../../src/errors/generic/Unauthorized'; -import InvalidTrustKey from '../../src/errors/InvalidTrustKey'; - -const worker = WORKERS.SINGLE; - -describe('services/LocalWorker', () => -{ - describe('.isHealthy()', () => - { - it('should be healthy', async () => - { - const healthy = await worker.isHealthy(); - - expect(healthy).toBeTruthy(); - }); - }); - - describe('.hasProcedure(name)', () => - { - it('should find public procedures', () => - { - const hasSecondProcedure = worker.hasProcedure('second'); - const hasThirdProcedure = worker.hasProcedure('third'); - - expect(hasSecondProcedure).toBeTruthy(); - expect(hasThirdProcedure).toBeTruthy(); - }); - - it('should find protected procedures', () => - { - const hasProtectedProcedure = worker.hasProcedure('protected'); - - expect(hasProtectedProcedure).toBeTruthy(); - }); - - it('should not find private procedures', () => - { - const hasPrivateProcedure = worker.hasProcedure('private'); - const hasFirstProcedure = worker.hasProcedure('first'); - - expect(hasPrivateProcedure).toBeFalsy(); - expect(hasFirstProcedure).toBeFalsy(); - }); - - it('should not find non-existing procedures', () => - { - const hasNonExistingProcedure = worker.hasProcedure('nonExisting'); - - expect(hasNonExistingProcedure).toBeFalsy(); - }); - }); - - describe('.run(name, version, parameters)', () => - { - it('should run a public procedure that calls a private procedure on the same segment', async () => - { - const request = new Request('second', Version.DEFAULT, new Map(), new Map()); - const response = await worker.run(request); - - expect(response.result).toBe('first'); - }); - - it('should run a public procedure that calls a private procedure on another segment', async () => - { - const request = new Request('sixth', Version.DEFAULT, new Map(), new Map()); - const response = await worker.run(request); - - expect(response.result).toBe('first'); - }); - - it('should run a public procedure that calls a public procedure on another segment', async () => - { - const request = new Request('third', Version.DEFAULT, new Map(), new Map()); - const response = await worker.run(request); - - expect(response.result).toBe('fourth'); - }); - - it('should not run a non-existing procedure', async () => - { - const request = new Request('nonExisting', Version.DEFAULT, new Map(), new Map()); - const run = async () => worker.run(request); - - expect(run).rejects.toEqual(new ProcedureNotFound('nonExisting')); - }); - - it('should run a protected procedure with valid trust key', async () => - { - const headers = new Map().set('x-jitar-trust-key', TRUST_KEY); - const request = new Request('protected', Version.DEFAULT, new Map(), headers); - const response = await worker.run(request); - - expect(response.result).toBe('protected'); - }); - - it('should not run a protected procedure with invalid trust key', async () => - { - const headers = new Map().set('x-jitar-trust-key', 'invalid'); - const request = new Request('protected', Version.DEFAULT, new Map(), headers); - const run = async () => worker.run(request); - - expect(run).rejects.toEqual(new InvalidTrustKey()); - }); - - it('should not run a protected procedure without trust key', async () => - { - const request = new Request('protected', Version.DEFAULT, new Map(), new Map()); - const run = async () => worker.run(request); - - expect(run).rejects.toEqual(new Unauthorized()); - }); - }); -}); diff --git a/packages/runtime/test/services/Remote.spec.ts b/packages/runtime/test/services/Remote.spec.ts deleted file mode 100644 index aa384483..00000000 --- a/packages/runtime/test/services/Remote.spec.ts +++ /dev/null @@ -1,42 +0,0 @@ - -import { describe, expect, it } from 'vitest'; - -import { REQUESTS, remote, CONTENT_TYPE } from '../_fixtures/services/Remote.fixture'; - -describe('services/Remote', () => -{ - describe('.run', () => - { - it('should return the response as boolean', async() => - { - const response = await remote.run(REQUESTS.BOOLEAN_REQUEST); - - expect(response.result).toBe(false); - expect(response.getHeader(CONTENT_TYPE)).toBe('application/boolean'); - }); - - it('should return the response as number', async() => - { - const response = await remote.run(REQUESTS.NUMBER_REQUEST); - - expect(response.result).toBe(42); - expect(response.getHeader(CONTENT_TYPE)).toBe('application/number'); - }); - - it('should return the response as object', async() => - { - const response = await remote.run(REQUESTS.OBJECT_REQUEST); - - expect(response.result).toEqual({ result: 42 }); - expect(response.getHeader(CONTENT_TYPE)).toBe('application/json'); - }); - - it('should return the response as string', async() => - { - const response = await remote.run(REQUESTS.DEFAULT_REQUEST); - - expect(response.result).toBe('Sorry, try again'); - expect(response.getHeader(CONTENT_TYPE)).toBe('text/plain'); - }); - }); -}); diff --git a/packages/runtime/test/services/RemoteWorker.spec.ts b/packages/runtime/test/services/RemoteWorker.spec.ts deleted file mode 100644 index b6174d2d..00000000 --- a/packages/runtime/test/services/RemoteWorker.spec.ts +++ /dev/null @@ -1,47 +0,0 @@ - -import { describe, expect, it } from 'vitest'; - -import { WORKERS, WORKER_URL } from '../_fixtures/services/RemoteWorker.fixture'; - -const worker = WORKERS.REMOTE; - -describe('services/RemoteWorker', () => -{ - describe('.url', () => - { - it('should contain an url', () => - { - expect(worker.url).toContain(WORKER_URL); - }); - }); - - describe('.getProcedureNames()', () => - { - it('should contain all registered procedure', () => - { - const names = worker.getProcedureNames(); - - expect(names).toContain('first'); - expect(names).toContain('second'); - }); - }); - - describe('.hasProcedure(name)', () => - { - it('should find a procedure', () => - { - const hasFirstProcedure = worker.hasProcedure('first'); - const hasSecondProcedure = worker.hasProcedure('second'); - - expect(hasFirstProcedure).toBeTruthy(); - expect(hasSecondProcedure).toBeTruthy(); - }); - - it('should not find a procedure', () => - { - const hasNoProcedure = worker.hasProcedure('third'); - - expect(hasNoProcedure).toBeFalsy(); - }); - }); -}); diff --git a/packages/runtime/test/services/WorkerBalancer.spec.ts b/packages/runtime/test/services/WorkerBalancer.spec.ts deleted file mode 100644 index 3d485309..00000000 --- a/packages/runtime/test/services/WorkerBalancer.spec.ts +++ /dev/null @@ -1,41 +0,0 @@ - -import { describe, expect, it } from 'vitest'; - -import NoWorkerAvailable from '../../src/errors/NoWorkerAvailable'; -import Request from '../../src/models/Request'; -import Version from '../../src/models/Version'; - -import { BALANCERS, WORKERS } from '../_fixtures/services/WorkerBalancer.fixture'; - -const balancer = BALANCERS.FILLED; -const emptyBalancer = BALANCERS.EMPTY; - -describe('services/WorkerBalancer', () => -{ - describe('.getNextWorker()', () => - { - it('should select workers round robin', async () => - { - const firstSelectedWorker = balancer.getNextWorker(); - const secondSelectedWorker = balancer.getNextWorker(); - const thirdSelectedWorker = balancer.getNextWorker(); - const fourthSelectedWorker = balancer.getNextWorker(); - - expect(firstSelectedWorker).toBe(WORKERS.FIRST); - expect(secondSelectedWorker).toBe(WORKERS.SECOND); - expect(thirdSelectedWorker).toBe(WORKERS.FIRST); - expect(fourthSelectedWorker).toBe(WORKERS.SECOND); - }); - }); - - describe('.run(name, version, parameters)', () => - { - it('should throw a worker not available error', async () => - { - const request = new Request('nonExisting', Version.DEFAULT, new Map(), new Map()); - const run = async () => emptyBalancer.run(request); - - expect(run).rejects.toEqual(new NoWorkerAvailable('nonExisting')); - }); - }); -}); diff --git a/packages/runtime/test/services/WorkerMonitor.spec.ts b/packages/runtime/test/services/WorkerMonitor.spec.ts deleted file mode 100644 index 566b7898..00000000 --- a/packages/runtime/test/services/WorkerMonitor.spec.ts +++ /dev/null @@ -1,30 +0,0 @@ - -import { describe, expect, it } from 'vitest'; - -import { MONITORS, GATEWAY, WORKERS } from '../_fixtures/services/WorkerMonitor.fixture'; - -const monitor = MONITORS.HEALTH; - -describe('services/WorkerMonitor', () => -{ - describe('.monitor()', () => - { - it('should keep a worker and remove a worker', async () => - { - const beforeWorkers = GATEWAY.workers; - - expect(beforeWorkers.length).toBe(2); - expect(beforeWorkers[0]).toBe(WORKERS.GOOD); - expect(beforeWorkers[1]).toBe(WORKERS.BAD); - - monitor.start(); - await new Promise(resolve => setTimeout(resolve, 300)); - monitor.stop(); - - const afterWorkers = GATEWAY.workers; - - expect(afterWorkers.length).toBe(1); - expect(afterWorkers[0]).toBe(WORKERS.GOOD); - }); - }); -}); diff --git a/packages/runtime/test/utils/ClientIdHelper.spec.ts b/packages/runtime/test/utils/ClientIdHelper.spec.ts deleted file mode 100644 index 29ad73af..00000000 --- a/packages/runtime/test/utils/ClientIdHelper.spec.ts +++ /dev/null @@ -1,47 +0,0 @@ - -import { describe, expect, it } from 'vitest'; - -import ClientIdHelper from '../../src/utils/ClientIdHelper'; - -const clientIdHelper = new ClientIdHelper(); - -describe('services/ClientId', () => -{ - describe('.generate()', () => - { - it('should generate a client id', () => - { - const clientId = clientIdHelper.generate(); - - expect(clientId).toMatch(/^CLIENT_\d+$/); - }); - - it('should generate a different client id each time', () => - { - const clientId1 = clientIdHelper.generate(); - const clientId2 = clientIdHelper.generate(); - - expect(clientId1).not.toBe(clientId2); - }); - }); - - describe('.validate(clientId)', () => - { - it('should return true if the client id is valid', () => - { - expect(clientIdHelper.validate('CLIENT_0')).toBe(true); - expect(clientIdHelper.validate('CLIENT_42')).toBe(true); - expect(clientIdHelper.validate('CLIENT_007')).toBe(true); - }); - - it('should return false if the client id is invalid', () => - { - expect(clientIdHelper.validate('CLIENT_')).toBe(false); - expect(clientIdHelper.validate('CLIENT_X')).toBe(false); - expect(clientIdHelper.validate('CLIENT_0a')).toBe(false); - expect(clientIdHelper.validate('CLIENT_0.1')).toBe(false); - expect(clientIdHelper.validate('Client_0')).toBe(false); - expect(clientIdHelper.validate('SERVER_0')).toBe(false); - }); - }); -}); diff --git a/packages/runtime/test/utils/ModuleLoader.spec.ts b/packages/runtime/test/utils/ModuleLoader.spec.ts deleted file mode 100644 index 9ad777d4..00000000 --- a/packages/runtime/test/utils/ModuleLoader.spec.ts +++ /dev/null @@ -1,52 +0,0 @@ - -import { describe, expect, it } from 'vitest'; - -import ModuleNotAccessible from '../../src/errors/ModuleNotAccessible'; -import ModuleNotLoaded from '../../src/errors/ModuleNotLoaded'; -import ModuleLoader from '../../src/utils/ModuleLoader'; - -import { moduleImporter } from '../_fixtures/utils/ModuleLoader.fixture'; - -ModuleLoader.setImporter(moduleImporter); -ModuleLoader.setBaseUrl('/root/app/'); - -describe('utils/ModuleLoader', () => -{ - describe('.load(specifier)', () => - { - it('should load an existing specifier with extension from the base URL', async () => - { - const module = await ModuleLoader.load('./public/app.js'); - - expect(module.default).toBeInstanceOf(Function); - }); - - it('should load an existing specifier without extension from the base URL', async () => - { - const module = await ModuleLoader.load('./public/app'); - - expect(module.default).toBeInstanceOf(Function); - }); - - it('should load a system import specifier', async () => - { - const module = await ModuleLoader.load('jitar'); - - expect(module.default).toBeInstanceOf(Function); - }); - - it('should not load non-existing specifier from the base URL', () => - { - const run = () => ModuleLoader.load('./public/non-existing.js'); - - expect(run).rejects.toEqual(new ModuleNotLoaded('public/non-existing.js', 'Not found')); - }); - - it('should not allow URLs outside the base URL', () => - { - const run = () => ModuleLoader.load('../secrets.js'); - - expect(run).rejects.toEqual(new ModuleNotAccessible('../secrets.js')); - }); - }); -}); diff --git a/packages/runtime/test/utils/UrlRewriter.spec.ts b/packages/runtime/test/utils/UrlRewriter.spec.ts deleted file mode 100644 index a3415cec..00000000 --- a/packages/runtime/test/utils/UrlRewriter.spec.ts +++ /dev/null @@ -1,63 +0,0 @@ - -import { describe, expect, it } from 'vitest'; - -import UrlRewriter from '../../src/utils/UrlRewriter'; - -const BASE_PATH = '/base/path/'; -const MY_FILE = 'sub/my-file.ext'; -const FULL_PATH = '/base/path/sub/my-file.ext'; - -describe('runtime/utils/UrlRewriter', () => -{ - describe('.addBase(url, base)', () => - { - it('should add the base to the url', () => - { - const result = UrlRewriter.addBase(MY_FILE, BASE_PATH); - - expect(result).toBe(FULL_PATH); - }); - - it('should add the base to the url without a slash', () => - { - const result = UrlRewriter.addBase(MY_FILE, '/base/path'); - - expect(result).toBe(FULL_PATH); - }); - - it('should add the base to the url with trailing base slash', () => - { - const result = UrlRewriter.addBase('/sub/my-file.ext', BASE_PATH); - - expect(result).toBe(FULL_PATH); - }); - - it('should remove last path part from the base', () => - { - const result = UrlRewriter.addBase('../sub/my-file.ext', BASE_PATH); - - expect(result).toBe('/base/sub/my-file.ext'); - }); - - it('should remove last path parts from the base', () => - { - const result = UrlRewriter.addBase('../../../sub/my-file.ext', '/base/path/to/folder'); - - expect(result).toBe('/base/sub/my-file.ext'); - }); - - it('should remove all parts from the base', () => - { - const result = UrlRewriter.addBase('../../../sub/my-file.ext', BASE_PATH); - - expect(result).toBe(MY_FILE); - }); - - it('should add the base to the url', () => - { - const result = UrlRewriter.addBase('./sub/my-file.ext', BASE_PATH); - - expect(result).toBe(FULL_PATH); - }); - }); -}); diff --git a/packages/serialization/README.md b/packages/serialization/README.md index 4022746c..a091a57b 100644 --- a/packages/serialization/README.md +++ b/packages/serialization/README.md @@ -1,169 +1,16 @@ # Jitar Serialization -This package provides extensible serialization for automating end-to-end data transportation. It's used in the [Jitar](https://jitar.dev) project, but can also be used as standalone library in any project. +This package provides extensible serialization for end-to-end data transportation for the [Jitar](https://jitar.dev) runtime. -By default it supports: +For more information about Jitar: + +* [Visit our website](https://jitar.dev) +* [Read the documentation](https://docs.jitar.dev). + +## Supported data types * Primitives (bool, number, string, etc.) * Arrays / Maps / Sets * Objects / Classes * Typed Arrays (Data buffer) - -You can also write and add your own. - -## Installation - -To add this package to your project run: - -```bash -npm install @jitar/serialization -``` - -## Basic usage - -Serializers are created by the `SerializerBuilder` class. Once a serializer is created it can be used for serialization and deserialization of all type of values mentioned above. - -```ts -import { SerializerBuilder } from '@jitar/serializer'; - -const set = new Set().add('apple').add('banana'); -const map = new Map().set('bicycle', 1).set('car', 2).set('plane', 3); -const array = [ set, map ]; - -const serializer = SerializerBuilder.build(); -const serializedArray = serializer.serialize(array); -const deserializedArray = serializer.deserialize(serializedArray); - -console.log(deserializedArray); - -// [ -// Set(2) { 'apple', 'banana' }, -// Map(3) { 'bicycle' => 1, 'car' => 2, 'plane' => 3 } -// ] -``` - -## Class serialization - -In order to (de)serialize class instances, the serializer needs to know the source location of the class so it can be imported and instantiated. The source must be defined as a class property. - -```ts -import { SerializerBuilder, Loadable } from '@jitar/serializer'; - -class Person -{ - #name: string; - #age: string; - - construction(name: string, age: string) - { - this.#name = name; - this.#age = age; - } - - get name() { return this.#name; } - get age() { return this.#name; } - - toString(): string { `${this.#name} is ${this.#age}` } -} - -// Set the source like this -(Person as Loadable).source = import.meta.url; - -const peter = new Person('Peter', 42); - -const serializer = SerializerBuilder.build(); -const serializedPeter = serializer.serialize(peter); -const deserializedPeter = serializer.deserialize(serializedPeter); - -console.log(deserializedPeter.toString()); // Peter is 42 -``` - -## Bring your own class loader - -The `SerializerBuilder` uses a default class loader if no other loader is provided. This is fine is most cases, but for other cases you can implement your own class loader. - -```ts -import { ClassLoader } from '@jitar/serializer'; - -export default class MyClassLoader implements ClassLoader -{ - async loadClass(loadable: Loadable): Promise - { - // Do your magic here. - // The Loadable type provides the source and the class name. - } -} -``` - -Simply provide your class loader to the `SerializerBuilder` to use it. - -```ts -import { SerializerBuilder } from '@jitar/serializer'; -import MyClassLoader from './MyClassLoader'; - -const serializer = SerializerBuilder.build(new MyClassLoader()); - -// The serializer now uses your class loader -``` - -## Extending the serializer - -You can write and add your own (de)serializers by extending the `ValueSerializer` and implement its four abstract functions. - -1. `canSerialize` - check if the value meets all the requirements to be serialized by this serializer. -1. `canDeserialize` - check if the value meets all the requirements to be deserialized by this serializer. -1. `serialize` - serialize the value. -1. `deserialize` - deserialize the value. - -For example, this is how the `ArraySerializer` looks like. - -```ts -import { ValueSerializer } from '@jitar/serializer'; - -export default class ArraySerializer extends ValueSerializer -{ - canSerialize(value: unknown): boolean - { - return value instanceof Array; - } - - canDeserialize(value: unknown): boolean - { - return value instanceof Array; - } - - async serialize(array: unknown[]): Promise - { - const values: unknown[] = []; - - for (const value of array) - { - values.push(await this.serializeOther(value)); - } - - return values; - } - - async deserialize(array: unknown[]): Promise - { - return await Promise.all(array.map(async (value) => await this.deserializeOther(value))); - } -} -``` - -The `this.deserializeOther(value)` function lets the main serializer handle the serialization of the (unknown) sub value. - -Value serializers can easily be added to the main serializer. - -```ts -import { SerializerBuilder } from '@jitar/serializer'; -import MySerializer from './MySerializer'; - -const serializer = SerializerBuilder.build(); -serializer.addSerializer(new MySerializer()); - -// Your serializer is now being used -``` - -Last added value serializers are first in line to check if they can (de)serialize a value. This means that you can override any of the default serializers. diff --git a/packages/serialization/package.json b/packages/serialization/package.json index f274cb5a..46855440 100644 --- a/packages/serialization/package.json +++ b/packages/serialization/package.json @@ -1,44 +1,21 @@ { "name": "@jitar/serialization", - "version": "0.7.5", - "description": "JavaScript serialization library for the Jitar runtime.", + "version": "0.7.4", + "description": "Serialization library for the Jitar runtime.", "author": "Masking Technology (https://jitar.dev)", "license": "MIT", "type": "module", - "types": "dist/lib.d.ts", - "exports": { - ".": "./dist/lib.js" - }, - "files": [ - "CHANGELOG.md", - "README.md", - "dist" - ], - "publishConfig": { - "access": "public" - }, + "types": "dist/index.d.ts", + "exports": "./dist/index.js", + "private": true, "scripts": { "test": "vitest run", "test-coverage": "vitest run --coverage", "lint": "eslint . --ext .ts", "build": "tsc -p tsconfig.json", - "clean": "rm -rf dist", - "prepublishOnly": "npm run clean && npm run build" + "clean": "rm -rf dist" }, "dependencies": { - "@jitar/reflection": "*" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/MaskingTechnology/jitar.git" - }, - "bugs": { - "url": "https://github.com/MaskingTechnology/jitar/issues" - }, - "homepage": "https://jitar.dev", - "keywords": [ - "javascript", - "serialization", - "jitar" - ] + "@jitar/analysis": "*" + } } diff --git a/packages/serialization/src/DefaultClassLoader.ts b/packages/serialization/src/DefaultClassLoader.ts deleted file mode 100644 index 4a02d8de..00000000 --- a/packages/serialization/src/DefaultClassLoader.ts +++ /dev/null @@ -1,30 +0,0 @@ - -import ClassNotFound from './errors/ClassNotFound.js'; -import InvalidClass from './errors/InvalidClass.js'; -import ClassLoader from './interfaces/ClassLoader.js'; -import Loadable from './types/Loadable.js'; - -export default class DefaultClassLoader implements ClassLoader -{ - async loadClass(loadable: Loadable): Promise - { - if (typeof loadable.source !== 'string') - { - throw new ClassNotFound(loadable.name); - } - - const module = await import(loadable.source); - const clazz = (module[loadable.name] ?? module['default']) as Function; - - if (clazz === undefined) - { - throw new ClassNotFound(loadable.name); - } - else if ((clazz instanceof Function) === false) - { - throw new InvalidClass(loadable.name); - } - - return clazz; - } -} diff --git a/packages/serialization/src/Serializer.ts b/packages/serialization/src/Serializer.ts index 0c3b36e9..2c27c84e 100644 --- a/packages/serialization/src/Serializer.ts +++ b/packages/serialization/src/Serializer.ts @@ -1,7 +1,7 @@ -import ValueSerializer from './ValueSerializer.js'; -import NoDeserializerFound from './errors/NoDeserializerFound.js'; -import NoSerializerFound from './errors/NoSerializerFound.js'; +import ValueSerializer from './ValueSerializer'; +import NoDeserializerFound from './errors/NoDeserializerFound'; +import NoSerializerFound from './errors/NoSerializerFound'; export default class Serializer { diff --git a/packages/serialization/src/SerializerBuilder.ts b/packages/serialization/src/SerializerBuilder.ts index 5fe4bb52..b033fd77 100644 --- a/packages/serialization/src/SerializerBuilder.ts +++ b/packages/serialization/src/SerializerBuilder.ts @@ -1,30 +1,33 @@ -import Serializer from './Serializer.js'; -import ClassLoader from './interfaces/ClassLoader.js'; -import ArraySerializer from './serializers/ArraySerializer.js'; -import BigIntSerializer from './serializers/BigIntSerializer.js'; -import ClassSerializer from './serializers/ClassSerializer.js'; -import DateSerializer from './serializers/DateSerializer.js'; -import ErrorSerializer from './serializers/ErrorSerializer.js'; -import MapSerializer from './serializers/MapSerializer.js'; -import ObjectSerializer from './serializers/ObjectSerializer.js'; -import PrimitiveSerializer from './serializers/PrimitiveSerializer.js'; -import RegExpSerializer from './serializers/RegExpSerializer.js'; -import SetSerializer from './serializers/SetSerializer.js'; -import TypedArraySerializer from './serializers/TypedArraySerializer.js'; -import UrlSerializer from './serializers/UrlSerializer.js'; -import DefaultClassLoader from './DefaultClassLoader.js'; - -const defaultClassLoader = new DefaultClassLoader(); +import Serializer from './Serializer'; +import ClassResolver from './interfaces/ClassResolver'; +import ArraySerializer from './serializers/ArraySerializer'; +import BigIntSerializer from './serializers/BigIntSerializer'; +import ClassSerializer from './serializers/ClassSerializer'; +import DateSerializer from './serializers/DateSerializer'; +import ErrorSerializer from './serializers/ErrorSerializer'; +import MapSerializer from './serializers/MapSerializer'; +import ObjectSerializer from './serializers/ObjectSerializer'; +import PrimitiveSerializer from './serializers/PrimitiveSerializer'; +import RegExpSerializer from './serializers/RegExpSerializer'; +import SetSerializer from './serializers/SetSerializer'; +import TypedArraySerializer from './serializers/TypedArraySerializer'; +import UrlSerializer from './serializers/UrlSerializer'; export default class SerializerBuilder { - public static build(loader: ClassLoader = defaultClassLoader): Serializer + public static build(classResolver?: ClassResolver): Serializer { const serializer = new Serializer(); + serializer.addSerializer(new PrimitiveSerializer()); serializer.addSerializer(new ObjectSerializer()); - serializer.addSerializer(new ClassSerializer(loader)); + + if (classResolver !== undefined) + { + serializer.addSerializer(new ClassSerializer(classResolver)); + } + serializer.addSerializer(new ErrorSerializer()); serializer.addSerializer(new RegExpSerializer()); serializer.addSerializer(new BigIntSerializer()); diff --git a/packages/serialization/src/ValueSerializer.ts b/packages/serialization/src/ValueSerializer.ts index 4f9d148e..9909e5d9 100644 --- a/packages/serialization/src/ValueSerializer.ts +++ b/packages/serialization/src/ValueSerializer.ts @@ -1,6 +1,6 @@ -import Serializer from './Serializer.js'; -import ParentSerializerNotSet from './errors/ParentSerializerNotSet.js'; +import Serializer from './Serializer'; +import ParentSerializerNotSet from './errors/ParentSerializerNotSet'; export default abstract class ValueSerializer { diff --git a/packages/serialization/src/index.ts b/packages/serialization/src/index.ts new file mode 100644 index 00000000..fbfa2614 --- /dev/null +++ b/packages/serialization/src/index.ts @@ -0,0 +1,16 @@ + +// Errors +export { default as ClassNotFound } from './errors/ClassNotFound'; +export { default as InvalidClass } from './errors/InvalidClass'; + +// Interfaces +export { default as ClassResolver } from './interfaces/ClassResolver'; + +// Types +export { default as Resolvable } from './types/Resolvable'; +export { default as Serialized } from './types/Serialized'; + +// Root +export { default as Serializer } from './Serializer'; +export { default as SerializerBuilder } from './SerializerBuilder'; +export { default as ValueSerializer } from './ValueSerializer'; diff --git a/packages/serialization/src/interfaces/ClassLoader.ts b/packages/serialization/src/interfaces/ClassLoader.ts deleted file mode 100644 index 80c7ded7..00000000 --- a/packages/serialization/src/interfaces/ClassLoader.ts +++ /dev/null @@ -1,9 +0,0 @@ - -import Loadable from '../types/Loadable.js'; - -interface ClassLoader -{ - loadClass(loadable: Loadable): Promise; -} - -export default ClassLoader; diff --git a/packages/serialization/src/interfaces/ClassResolver.ts b/packages/serialization/src/interfaces/ClassResolver.ts new file mode 100644 index 00000000..80349656 --- /dev/null +++ b/packages/serialization/src/interfaces/ClassResolver.ts @@ -0,0 +1,9 @@ + +interface ClassResolver +{ + resolveKey(clazz: Function): string | undefined; + + resolveClass(key: string): Function | undefined; +} + +export default ClassResolver; diff --git a/packages/serialization/src/lib.ts b/packages/serialization/src/lib.ts deleted file mode 100644 index 2d9a78cc..00000000 --- a/packages/serialization/src/lib.ts +++ /dev/null @@ -1,16 +0,0 @@ - -// Errors -export { default as ClassNotFound } from './errors/ClassNotFound.js'; -export { default as InvalidClass } from './errors/InvalidClass.js'; - -// Interfaces -export { default as ClassLoader } from './interfaces/ClassLoader.js'; - -// Types -export { default as Loadable } from './types/Loadable.js'; -export { default as Serialized } from './types/Serialized.js'; - -// Root -export { default as Serializer } from './Serializer.js'; -export { default as SerializerBuilder } from './SerializerBuilder.js'; -export { default as ValueSerializer } from './ValueSerializer.js'; diff --git a/packages/serialization/src/serializers/ArraySerializer.ts b/packages/serialization/src/serializers/ArraySerializer.ts index ecf9c847..2584fa2e 100644 --- a/packages/serialization/src/serializers/ArraySerializer.ts +++ b/packages/serialization/src/serializers/ArraySerializer.ts @@ -1,5 +1,5 @@ -import ValueSerializer from '../ValueSerializer.js'; +import ValueSerializer from '../ValueSerializer'; export default class ArraySerializer extends ValueSerializer { diff --git a/packages/serialization/src/serializers/BigIntSerializer.ts b/packages/serialization/src/serializers/BigIntSerializer.ts index ea55d00e..26b395b3 100644 --- a/packages/serialization/src/serializers/BigIntSerializer.ts +++ b/packages/serialization/src/serializers/BigIntSerializer.ts @@ -1,7 +1,7 @@ -import ValueSerializer from '../ValueSerializer.js'; -import SerializedBigInt from '../types/serialized/SerializedBigInt.js'; -import InvalidBigIntString from './errors/InvalidBigIntString.js'; +import ValueSerializer from '../ValueSerializer'; +import SerializedBigInt from '../types/serialized/SerializedBigInt'; +import InvalidBigIntString from './errors/InvalidBigIntString'; export default class BigIntSerializer extends ValueSerializer { diff --git a/packages/serialization/src/serializers/ClassSerializer.ts b/packages/serialization/src/serializers/ClassSerializer.ts index 2e94cb41..9d56cb47 100644 --- a/packages/serialization/src/serializers/ClassSerializer.ts +++ b/packages/serialization/src/serializers/ClassSerializer.ts @@ -1,25 +1,26 @@ -import { ReflectionClass, ReflectionField, Reflector } from '@jitar/reflection'; +import { ESClass, ESField, Reflector } from '@jitar/analysis'; -import ValueSerializer from '../ValueSerializer.js'; -import ClassNotFound from '../errors/ClassNotFound.js'; -import InvalidClass from '../errors/InvalidClass.js'; -import ClassLoader from '../interfaces/ClassLoader.js'; -import Loadable from '../types/Loadable.js'; -import SerializableObject from '../types/serialized/SerializableObject.js'; -import SerializedClass from '../types/serialized/SerializedClass.js'; +import ValueSerializer from '../ValueSerializer'; +import ClassNotFound from '../errors/ClassNotFound'; +import InvalidClass from '../errors/InvalidClass'; +import ClassResolver from '../interfaces/ClassResolver'; +import FlexObject from '../types/serialized/SerializableObject'; +import SerializableObject from '../types/serialized/SerializableObject'; +import SerializedClass from '../types/serialized/SerializedClass'; +import Resolvable from '../types/Resolvable'; const reflector = new Reflector(); export default class ClassSerializer extends ValueSerializer { - #classLoader: ClassLoader; + #classResolver: ClassResolver; - constructor(classLoader: ClassLoader) + constructor(classResolver: ClassResolver) { super(); - this.#classLoader = classLoader; + this.#classResolver = classResolver; } canSerialize(value: unknown): boolean @@ -34,7 +35,8 @@ export default class ClassSerializer extends ValueSerializer return object instanceof Object && object.serialized === true - && typeof object.name === 'string' + && object.name === 'class' + && typeof object.key === 'string' && object.args instanceof Object && object.args.constructor === Object && object.fields instanceof Object @@ -47,41 +49,49 @@ export default class ClassSerializer extends ValueSerializer const model = reflector.fromClass(clazz, true); const parameterNames = this.#extractConstructorParameters(model); - const name = clazz.name; - const source = (clazz as Loadable).source; - const args: SerializableObject = await this.#serializeConstructor(model, parameterNames, object); - const fields: SerializableObject = await this.#serializeFields(model, parameterNames, object); + const name = 'class'; + const key = this.#classResolver.resolveKey(clazz); - return { serialized: true, name: name, source: source, args: args, fields: fields }; + if (key === undefined) + { + throw new ClassNotFound(clazz.name); + } + + const args: FlexObject = await this.#serializeConstructor(model, parameterNames, object); + const fields: FlexObject = await this.#serializeFields(model, parameterNames, object); + + return { serialized: true, key, name, args, fields }; } - #extractConstructorParameters(model: ReflectionClass): string[] + #extractConstructorParameters(model: ESClass): string[] { const constructor = model.getFunction('constructor'); - const parameters = (constructor?.parameters ?? []) as ReflectionField[]; + const parameters = (constructor?.parameters ?? []) as ESField[]; return parameters.map(parameter => parameter.name); } - async #serializeConstructor(model: ReflectionClass, includeNames: string[], object: object): Promise + async #serializeConstructor(model: ESClass, includeNames: string[], object: object): Promise { const args: SerializableObject = {}; - for (const name of includeNames) + for (const [index, name] of includeNames.entries()) { // Constructor parameters that can't be read make it impossible to fully reconstruct the object. - - const objectValue = model.canRead(name) - ? await this.serializeOther((object as SerializableObject)[name]) + + const value = model.canRead(name) + ? await this.serializeOther((object as FlexObject)[name]) : undefined; - args[name] = objectValue; + const key = index.toString(); + + args[key] = value; } return args; } - async #serializeFields(model: ReflectionClass, excludeNames: string[], object: object): Promise + async #serializeFields(model: ESClass, excludeNames: string[], object: object): Promise { const fields: SerializableObject = {}; @@ -108,11 +118,11 @@ export default class ClassSerializer extends ValueSerializer if (clazz === undefined) { - throw new ClassNotFound(object.name); + throw new ClassNotFound(object.key); } else if ((clazz instanceof Function) === false) { - throw new InvalidClass(object.name); + throw new InvalidClass(object.key); } const args = await this.#deserializeConstructor(clazz, object.args); @@ -133,11 +143,12 @@ export default class ClassSerializer extends ValueSerializer { const model = reflector.fromClass(clazz, true); const constructor = model.getFunction('constructor'); - const parameters = (constructor?.parameters ?? []) as ReflectionField[]; + const parameters = (constructor?.parameters ?? []) as ESField[]; - const values = parameters.map(parameter => + const values = parameters.map((_, index) => { - const value = args[parameter.name]; + const key = index.toString(); + const value = args[key]; return this.deserializeOther(value); }); @@ -145,13 +156,9 @@ export default class ClassSerializer extends ValueSerializer return Promise.all(values); } - async #getClass(loadable: Loadable): Promise + async #getClass(resolvable: Resolvable): Promise { - if (loadable.source === undefined) - { - return (globalThis as SerializableObject)[loadable.name]; - } - - return this.#classLoader.loadClass(loadable); + return (globalThis as FlexObject)[resolvable.key] + ?? this.#classResolver.resolveClass(resolvable.key); } } diff --git a/packages/serialization/src/serializers/DateSerializer.ts b/packages/serialization/src/serializers/DateSerializer.ts index b89f285a..9096551f 100644 --- a/packages/serialization/src/serializers/DateSerializer.ts +++ b/packages/serialization/src/serializers/DateSerializer.ts @@ -1,7 +1,7 @@ -import ValueSerializer from '../ValueSerializer.js'; -import SerializedDate from '../types/serialized/SerializedDate.js'; -import InvalidDateString from './errors/InvalidDateString.js'; +import ValueSerializer from '../ValueSerializer'; +import SerializedDate from '../types/serialized/SerializedDate'; +import InvalidDateString from './errors/InvalidDateString'; export default class DateSerializer extends ValueSerializer { diff --git a/packages/serialization/src/serializers/ErrorSerializer.ts b/packages/serialization/src/serializers/ErrorSerializer.ts index dd18ae09..dad1aa05 100644 --- a/packages/serialization/src/serializers/ErrorSerializer.ts +++ b/packages/serialization/src/serializers/ErrorSerializer.ts @@ -1,6 +1,6 @@ -import ValueSerializer from '../ValueSerializer.js'; -import SerializedError from '../types/serialized/SerializedError.js'; +import ValueSerializer from '../ValueSerializer'; +import SerializedError from '../types/serialized/SerializedError'; export default class ErrorSerializer extends ValueSerializer { diff --git a/packages/serialization/src/serializers/MapSerializer.ts b/packages/serialization/src/serializers/MapSerializer.ts index 3785ee98..c7906234 100644 --- a/packages/serialization/src/serializers/MapSerializer.ts +++ b/packages/serialization/src/serializers/MapSerializer.ts @@ -1,6 +1,6 @@ -import ValueSerializer from '../ValueSerializer.js'; -import SerializedMap from '../types/serialized/SerializedMap.js'; +import ValueSerializer from '../ValueSerializer'; +import SerializedMap from '../types/serialized/SerializedMap'; export default class MapSerializer extends ValueSerializer { diff --git a/packages/serialization/src/serializers/ObjectSerializer.ts b/packages/serialization/src/serializers/ObjectSerializer.ts index 2e491fb2..a48e0ca0 100644 --- a/packages/serialization/src/serializers/ObjectSerializer.ts +++ b/packages/serialization/src/serializers/ObjectSerializer.ts @@ -1,7 +1,7 @@ -import ValueSerializer from '../ValueSerializer.js'; -import SerializableObject from '../types/serialized/SerializableObject.js'; -import SerializedObject from '../types/serialized/SerializedObject.js'; +import ValueSerializer from '../ValueSerializer'; +import SerializableObject from '../types/serialized/SerializableObject'; +import SerializedObject from '../types/serialized/SerializedObject'; export default class ObjectSerializer extends ValueSerializer { diff --git a/packages/serialization/src/serializers/PrimitiveSerializer.ts b/packages/serialization/src/serializers/PrimitiveSerializer.ts index 1bc25416..1e3d964a 100644 --- a/packages/serialization/src/serializers/PrimitiveSerializer.ts +++ b/packages/serialization/src/serializers/PrimitiveSerializer.ts @@ -1,5 +1,5 @@ -import ValueSerializer from '../ValueSerializer.js'; +import ValueSerializer from '../ValueSerializer'; export default class PrimitiveSerializer extends ValueSerializer { diff --git a/packages/serialization/src/serializers/RegExpSerializer.ts b/packages/serialization/src/serializers/RegExpSerializer.ts index 2fa40bcd..26e0d3d8 100644 --- a/packages/serialization/src/serializers/RegExpSerializer.ts +++ b/packages/serialization/src/serializers/RegExpSerializer.ts @@ -1,7 +1,7 @@ -import ValueSerializer from '../ValueSerializer.js'; -import SerializedRegExp from '../types/serialized/SerializedRegExp.js'; -import InvalidRegExp from './errors/InvalidRegExp.js'; +import ValueSerializer from '../ValueSerializer'; +import SerializedRegExp from '../types/serialized/SerializedRegExp'; +import InvalidRegExp from './errors/InvalidRegExp'; export default class RegExpSerializer extends ValueSerializer { diff --git a/packages/serialization/src/serializers/SetSerializer.ts b/packages/serialization/src/serializers/SetSerializer.ts index 532b404c..44ecd9ad 100644 --- a/packages/serialization/src/serializers/SetSerializer.ts +++ b/packages/serialization/src/serializers/SetSerializer.ts @@ -1,6 +1,6 @@ -import ValueSerializer from '../ValueSerializer.js'; -import SerializedSet from '../types/serialized/SerializedSet.js'; +import ValueSerializer from '../ValueSerializer'; +import SerializedSet from '../types/serialized/SerializedSet'; export default class SetSerializer extends ValueSerializer { diff --git a/packages/serialization/src/serializers/TypedArraySerializer.ts b/packages/serialization/src/serializers/TypedArraySerializer.ts index 211274a9..2bfd4db2 100644 --- a/packages/serialization/src/serializers/TypedArraySerializer.ts +++ b/packages/serialization/src/serializers/TypedArraySerializer.ts @@ -1,9 +1,9 @@ -import { Reflector } from '@jitar/reflection'; +import { Reflector } from '@jitar/analysis'; -import ValueSerializer from '../ValueSerializer.js'; -import SerializedTypedArray from '../types/serialized/SerializedTypedArray.js'; -import TypedArray from '../types/TypedArray.js'; +import ValueSerializer from '../ValueSerializer'; +import SerializedTypedArray from '../types/serialized/SerializedTypedArray'; +import TypedArray from '../types/TypedArray'; const reflector = new Reflector(); diff --git a/packages/serialization/src/serializers/UrlSerializer.ts b/packages/serialization/src/serializers/UrlSerializer.ts index a71741cb..9c0c4824 100644 --- a/packages/serialization/src/serializers/UrlSerializer.ts +++ b/packages/serialization/src/serializers/UrlSerializer.ts @@ -1,7 +1,7 @@ -import ValueSerializer from '../ValueSerializer.js'; -import SerializedUrl from '../types/serialized/SerializedUrl.js'; -import InvalidUrlString from './errors/InvalidUrlString.js'; +import ValueSerializer from '../ValueSerializer'; +import SerializedUrl from '../types/serialized/SerializedUrl'; +import InvalidUrlString from './errors/InvalidUrlString'; export default class UrlSerializer extends ValueSerializer { diff --git a/packages/serialization/src/types/Loadable.ts b/packages/serialization/src/types/Loadable.ts deleted file mode 100644 index cd3cf01a..00000000 --- a/packages/serialization/src/types/Loadable.ts +++ /dev/null @@ -1,8 +0,0 @@ - -type Loadable = -{ - name: string; - source?: string; -} - -export default Loadable; diff --git a/packages/serialization/src/types/Resolvable.ts b/packages/serialization/src/types/Resolvable.ts new file mode 100644 index 00000000..52999724 --- /dev/null +++ b/packages/serialization/src/types/Resolvable.ts @@ -0,0 +1,7 @@ + +type Resolvable = +{ + key: string; +} + +export default Resolvable; diff --git a/packages/serialization/src/types/serialized/SerializedBigInt.ts b/packages/serialization/src/types/serialized/SerializedBigInt.ts index ea41215a..f17be737 100644 --- a/packages/serialization/src/types/serialized/SerializedBigInt.ts +++ b/packages/serialization/src/types/serialized/SerializedBigInt.ts @@ -1,5 +1,5 @@ -import Serialized from '../Serialized.js'; +import Serialized from '../Serialized'; type SerializedBigInt = Serialized & { diff --git a/packages/serialization/src/types/serialized/SerializedClass.ts b/packages/serialization/src/types/serialized/SerializedClass.ts index f0a8bc2e..3521d438 100644 --- a/packages/serialization/src/types/serialized/SerializedClass.ts +++ b/packages/serialization/src/types/serialized/SerializedClass.ts @@ -1,8 +1,8 @@ -import Loadable from '../Loadable.js'; -import Serialized from '../Serialized.js'; +import Resolvable from '../Resolvable'; +import Serialized from '../Serialized'; -type SerializedClass = Serialized & Loadable & +type SerializedClass = Serialized & Resolvable & { args: Record, fields: Record diff --git a/packages/serialization/src/types/serialized/SerializedDate.ts b/packages/serialization/src/types/serialized/SerializedDate.ts index d94f75f9..3963d8e5 100644 --- a/packages/serialization/src/types/serialized/SerializedDate.ts +++ b/packages/serialization/src/types/serialized/SerializedDate.ts @@ -1,5 +1,5 @@ -import Serialized from '../Serialized.js'; +import Serialized from '../Serialized'; type SerializedDate = Serialized & { diff --git a/packages/serialization/src/types/serialized/SerializedError.ts b/packages/serialization/src/types/serialized/SerializedError.ts index bff77b9c..92e93247 100644 --- a/packages/serialization/src/types/serialized/SerializedError.ts +++ b/packages/serialization/src/types/serialized/SerializedError.ts @@ -1,5 +1,5 @@ -import Serialized from '../Serialized.js'; +import Serialized from '../Serialized'; type SerializedError = Serialized & { diff --git a/packages/serialization/src/types/serialized/SerializedMap.ts b/packages/serialization/src/types/serialized/SerializedMap.ts index 28840d64..01c627bd 100644 --- a/packages/serialization/src/types/serialized/SerializedMap.ts +++ b/packages/serialization/src/types/serialized/SerializedMap.ts @@ -1,5 +1,5 @@ -import Serialized from '../Serialized.js'; +import Serialized from '../Serialized'; type SerializedMap = Serialized & { diff --git a/packages/serialization/src/types/serialized/SerializedRegExp.ts b/packages/serialization/src/types/serialized/SerializedRegExp.ts index 3218788e..4a5201ab 100644 --- a/packages/serialization/src/types/serialized/SerializedRegExp.ts +++ b/packages/serialization/src/types/serialized/SerializedRegExp.ts @@ -1,5 +1,5 @@ -import Serialized from '../Serialized.js'; +import Serialized from '../Serialized'; type SerializedRegExp = Serialized & { diff --git a/packages/serialization/src/types/serialized/SerializedSet.ts b/packages/serialization/src/types/serialized/SerializedSet.ts index c196e3fc..f345b311 100644 --- a/packages/serialization/src/types/serialized/SerializedSet.ts +++ b/packages/serialization/src/types/serialized/SerializedSet.ts @@ -1,5 +1,5 @@ -import Serialized from '../Serialized.js'; +import Serialized from '../Serialized'; type SerializedSet = Serialized & { diff --git a/packages/serialization/src/types/serialized/SerializedTypedArray.ts b/packages/serialization/src/types/serialized/SerializedTypedArray.ts index 0bab49b6..ade44564 100644 --- a/packages/serialization/src/types/serialized/SerializedTypedArray.ts +++ b/packages/serialization/src/types/serialized/SerializedTypedArray.ts @@ -1,5 +1,5 @@ -import Serialized from '../Serialized.js'; +import Serialized from '../Serialized'; type SerializedTypedArray = Serialized & { diff --git a/packages/serialization/src/types/serialized/SerializedUrl.ts b/packages/serialization/src/types/serialized/SerializedUrl.ts index 210b614e..8367d401 100644 --- a/packages/serialization/src/types/serialized/SerializedUrl.ts +++ b/packages/serialization/src/types/serialized/SerializedUrl.ts @@ -1,5 +1,5 @@ -import Serialized from '../Serialized.js'; +import Serialized from '../Serialized'; type SerializedUrl = Serialized & { diff --git a/packages/serialization/test/DefaultClassLoader.spec.ts b/packages/serialization/test/DefaultClassLoader.spec.ts deleted file mode 100644 index 7dbc7686..00000000 --- a/packages/serialization/test/DefaultClassLoader.spec.ts +++ /dev/null @@ -1,51 +0,0 @@ - -import { describe, expect, it } from 'vitest'; - -import DefaultClassLoader from '../src/DefaultClassLoader'; -import ClassNotFound from '../src/errors/ClassNotFound'; -import InvalidClass from '../src/errors/InvalidClass'; - -import - { - Person, Customer, - personLoadable, customerLoadable, primitiveLoadable, - nonexistingLoadable, invalidLoadable - } from './_fixtures/DefaultClassLoader.fixture'; - -const loader = new DefaultClassLoader(); - -describe('DefaultClassLoader', () => -{ - describe('.loadClass(loadable)', () => - { - it('should load classes', async () => - { - const person = await loader.loadClass(personLoadable); - const customer = await loader.loadClass(customerLoadable); - - expect(person).toBe(Person); - expect(customer).toBe(Customer); - }); - - it('should not load non-class types', async () => - { - const loadClass = async () => loader.loadClass(primitiveLoadable); - - expect(loadClass).rejects.toStrictEqual(new InvalidClass('primitive')); - }); - - it('should not load non-existing classes', async () => - { - const loadClass = async () => loader.loadClass(nonexistingLoadable); - - expect(loadClass).rejects.toStrictEqual(new ClassNotFound('nonexisting')); - }); - - it('should not load from invalid sources', async () => - { - const loadClass = async () => loader.loadClass(invalidLoadable); - - expect(loadClass).rejects.toStrictEqual(new ClassNotFound('invalid')); - }); - }); -}); diff --git a/packages/serialization/test/Serializer.spec.ts b/packages/serialization/test/Serializer.spec.ts index f37f7371..1980cb17 100644 --- a/packages/serialization/test/Serializer.spec.ts +++ b/packages/serialization/test/Serializer.spec.ts @@ -1,23 +1,13 @@ import { describe, expect, it } from 'vitest'; -import Serializer from '../src/Serializer'; import NoSerializerFound from '../src/errors/NoSerializerFound'; import NoDeserializerFound from '../src/errors/NoDeserializerFound'; -import - { - FirstSerializer, SecondSerializer, - NumberSerializer, StringSerializer - } from './_fixtures/Serializer.fixture'; - -const overrideSerializer = new Serializer(); -overrideSerializer.addSerializer(new FirstSerializer()); -overrideSerializer.addSerializer(new SecondSerializer()); +import { SERIALIZERS } from './fixtures'; -const typeSerializer = new Serializer(); -typeSerializer.addSerializer(new NumberSerializer()); -typeSerializer.addSerializer(new StringSerializer()); +const overrideSerializer = SERIALIZERS.OVERRIDE; +const typeSerializer = SERIALIZERS.TYPE; describe('Serializer', () => { diff --git a/packages/serialization/test/SerializerBuilder.spec.ts b/packages/serialization/test/SerializerBuilder.spec.ts index a46d58fd..be42d176 100644 --- a/packages/serialization/test/SerializerBuilder.spec.ts +++ b/packages/serialization/test/SerializerBuilder.spec.ts @@ -3,60 +3,51 @@ import { describe, expect, it } from 'vitest'; import SerializerBuilder from '../src/SerializerBuilder'; -import { - classLoader, - mixedArray, mixedObject, - dataClass, serializedDataClass, - fixedDate, serializedFixedDate, - mixedMap, serializedMixedMap, - mixedSet, serializedMixedSet, - viewInt8, serializedViewInt8, - stringValue -} from './_fixtures/SerializerBuilder.fixture'; - -const serializer = SerializerBuilder.build(classLoader); +import { ARRAYS, OBJECTS, CLASSES, DATES, MAPS, SETS, TYPED_ARRAYS, PRIMITIVES, classResolver } from './serializers/fixtures'; + +const serializer = SerializerBuilder.build(classResolver); describe('SerializerBuilder', () => { it('should serialize mixed types with the build serializer', async () => { - const resultArray = await serializer.serialize(mixedArray); - const resultObject = await serializer.serialize(mixedObject); - const resultClass = await serializer.serialize(dataClass); - const resultDate = await serializer.serialize(fixedDate); - const resultMap = await serializer.serialize(mixedMap); - const resultSet = await serializer.serialize(mixedSet); - const resultView = await serializer.serialize(viewInt8); - const resultString = await serializer.serialize(stringValue); - - expect(resultArray).toStrictEqual(mixedArray); - expect(resultObject).toStrictEqual(mixedObject); - expect(resultClass).toStrictEqual(serializedDataClass); - expect(resultDate).toStrictEqual(serializedFixedDate); - expect(resultMap).toStrictEqual(serializedMixedMap); - expect(resultSet).toStrictEqual(serializedMixedSet); - expect(resultView).toStrictEqual(serializedViewInt8); - expect(resultString).toStrictEqual(stringValue); + const resultArray = await serializer.serialize(ARRAYS.MIXED); + const resultObject = await serializer.serialize(OBJECTS.MIXED); + const resultClass = await serializer.serialize(CLASSES.DATA_INSTANCE); + const resultDate = await serializer.serialize(DATES.FIXED); + const resultMap = await serializer.serialize(MAPS.MIXED); + const resultSet = await serializer.serialize(SETS.MIXED); + const resultView = await serializer.serialize(TYPED_ARRAYS.INT8); + const resultString = await serializer.serialize(PRIMITIVES.STRING); + + expect(resultArray).toStrictEqual(ARRAYS.MIXED); + expect(resultObject).toStrictEqual(OBJECTS.MIXED); + expect(resultClass).toStrictEqual(CLASSES.DATA_SERIALIZED); + expect(resultDate).toStrictEqual(DATES.FIXED_SERIALIZED); + expect(resultMap).toStrictEqual(MAPS.MIXED_SERIALIZED); + expect(resultSet).toStrictEqual(SETS.MIXED_SERIALIZED); + expect(resultView).toStrictEqual(TYPED_ARRAYS.INT8_SERIALIZED); + expect(resultString).toStrictEqual(PRIMITIVES.STRING); }); it('should deserialize mixed types with the build serializer', async () => { - const resultArray = await serializer.deserialize(mixedArray); - const resultObject = await serializer.deserialize(mixedObject); - const resultClass = await serializer.deserialize(serializedDataClass); - const resultDate = await serializer.deserialize(serializedFixedDate); - const resultMap = await serializer.deserialize(serializedMixedMap); - const resultSet = await serializer.deserialize(serializedMixedSet); - const resultView = await serializer.deserialize(serializedViewInt8); - const resultString = await serializer.deserialize(stringValue); - - expect(resultArray).toStrictEqual(mixedArray); - expect(resultObject).toStrictEqual(mixedObject); - expect(resultClass).toStrictEqual(dataClass); - expect(resultDate).toStrictEqual(fixedDate); - expect(resultMap).toStrictEqual(mixedMap); - expect(resultSet).toStrictEqual(mixedSet); - expect(resultView).toStrictEqual(viewInt8); - expect(resultString).toStrictEqual(stringValue); + const resultArray = await serializer.deserialize(ARRAYS.MIXED); + const resultObject = await serializer.deserialize(OBJECTS.MIXED); + const resultClass = await serializer.deserialize(CLASSES.DATA_SERIALIZED); + const resultDate = await serializer.deserialize(DATES.FIXED_SERIALIZED); + const resultMap = await serializer.deserialize(MAPS.MIXED_SERIALIZED); + const resultSet = await serializer.deserialize(SETS.MIXED_SERIALIZED); + const resultView = await serializer.deserialize(TYPED_ARRAYS.INT8_SERIALIZED); + const resultString = await serializer.deserialize(PRIMITIVES.STRING); + + expect(resultArray).toStrictEqual(ARRAYS.MIXED); + expect(resultObject).toStrictEqual(OBJECTS.MIXED); + expect(resultClass).toStrictEqual(CLASSES.DATA_INSTANCE); + expect(resultDate).toStrictEqual(DATES.FIXED); + expect(resultMap).toStrictEqual(MAPS.MIXED); + expect(resultSet).toStrictEqual(SETS.MIXED); + expect(resultView).toStrictEqual(TYPED_ARRAYS.INT8); + expect(resultString).toStrictEqual(PRIMITIVES.STRING); }); }); diff --git a/packages/serialization/test/_fixtures/DefaultClassLoader.fixture.ts b/packages/serialization/test/_fixtures/DefaultClassLoader.fixture.ts deleted file mode 100644 index 14a6a217..00000000 --- a/packages/serialization/test/_fixtures/DefaultClassLoader.fixture.ts +++ /dev/null @@ -1,48 +0,0 @@ - -import * as url from 'url'; - -import Loadable from '../../src/types/Loadable'; - -const __dirname = url.fileURLToPath(new URL('.', import.meta.url)); - -class Person {} - -class Customer extends Person {} - -const primitive = 42; - -const personLoadable: Loadable = -{ - name: 'Person', - source: `${__dirname}DefaultClassLoader.fixture.ts` -}; - -const customerLoadable: Loadable = -{ - name: 'Customer', - source: `${__dirname}DefaultClassLoader.fixture.ts` -}; - -const primitiveLoadable: Loadable = -{ - name: 'primitive', - source: `${__dirname}DefaultClassLoader.fixture.ts` -}; - -const nonexistingLoadable: Loadable = -{ - name: 'nonexisting', - source: `${__dirname}DefaultClassLoader.fixture.ts` -}; - -const invalidLoadable: Loadable = -{ - name: 'invalid', - source: null -}; - -export { - Person, Customer, primitive, - personLoadable, customerLoadable, primitiveLoadable, - nonexistingLoadable, invalidLoadable -}; diff --git a/packages/serialization/test/_fixtures/SerializerBuilder.fixture.ts b/packages/serialization/test/_fixtures/SerializerBuilder.fixture.ts deleted file mode 100644 index 7081a107..00000000 --- a/packages/serialization/test/_fixtures/SerializerBuilder.fixture.ts +++ /dev/null @@ -1,9 +0,0 @@ - -export { mixedArray } from './serializers/ArraySerializer.fixture'; -export { classLoader, dataClass, serializedDataClass } from './serializers/ClassSerializer.fixture'; -export { fixedDate, serializedFixedDate } from './serializers/DateSerializer.fixture'; -export { mixedMap, serializedMixedMap } from './serializers/MapSerializer.fixture'; -export { mixedObject } from './serializers/ObjectSerializer.fixture'; -export { stringValue } from './serializers/PrimitiveSerializer.fixture'; -export { mixedSet, serializedMixedSet } from './serializers/SetSerializer.fixture'; -export { viewInt8, serializedViewInt8 } from './serializers/TypedArraySerializer.fixture'; diff --git a/packages/serialization/test/_fixtures/serializers/ArraySerializer.fixture.ts b/packages/serialization/test/_fixtures/serializers/ArraySerializer.fixture.ts deleted file mode 100644 index 9352d490..00000000 --- a/packages/serialization/test/_fixtures/serializers/ArraySerializer.fixture.ts +++ /dev/null @@ -1,21 +0,0 @@ - -import Serializer from '../../../src/Serializer'; -import PrimitiveSerializer from '../../../src/serializers/PrimitiveSerializer'; -import ArraySerializer from '../../../src/serializers/ArraySerializer'; - -const parent = new Serializer(); -parent.addSerializer(new ArraySerializer()); -parent.addSerializer(new PrimitiveSerializer()); - -const emptyArray: unknown[] = []; -const mixedArray: unknown[] = ['a', 1, true]; -const nestedArray: unknown[] = ['b', 2, ['c', false], true]; - -const nonObject = 42; -const nonArray = new Map(); - -export { - parent, - emptyArray, mixedArray, nestedArray, - nonObject, nonArray -}; diff --git a/packages/serialization/test/_fixtures/serializers/ClassSerializer.fixture.ts b/packages/serialization/test/_fixtures/serializers/ClassSerializer.fixture.ts deleted file mode 100644 index d63d828d..00000000 --- a/packages/serialization/test/_fixtures/serializers/ClassSerializer.fixture.ts +++ /dev/null @@ -1,126 +0,0 @@ - -import Serializer from '../../../src/Serializer'; -import PrimitiveSerializer from '../../../src/serializers/PrimitiveSerializer'; -import ClassSerializer from '../../../src/serializers/ClassSerializer'; -import ClassLoader from '../../../src/interfaces/ClassLoader'; -import Loadable from '../../../src/types/Loadable'; - -class Data -{ - a?: number; - b?: boolean; -} - -(Data as Loadable).source = Data.name; - -class Constructed -{ - #a: number; - #b: boolean; - #c?: string; - - constructor(a: number, b: boolean) - { - this.#a = a; - this.#b = b; - } - - get a() { return this.#a; } - - get b() { return this.#b; } - - get c() { return this.#c; } - - set c(c) { this.#c = c; } -} - -(Constructed as Loadable).source = Constructed.name; - -class Nested -{ - name: string; - #constructed: Constructed; - - constructor(name: string, constructed: Constructed) - { - this.name = name; - this.#constructed = constructed; - } - - get constructed() { return this.#constructed; } -} - -(Nested as Loadable).source = Nested.name; - -class PrivateFieldClass -{ - #a: number; - #b: boolean; - #c?: string; - - constructor(a: number, b: boolean, c?: string) - { - this.#a = a; - this.#b = b; - this.#c = c; - } - - get d() { return this.#a; } -} - -(PrivateFieldClass as Loadable).source = PrivateFieldClass.name; - -class MockClassLoader implements ClassLoader -{ - async loadClass(loadable: Loadable): Promise - { - switch (loadable.source) - { - case 'Data': return Data; - case 'Constructed': return Constructed; - case 'Nested': return Nested; - case 'PrivateFieldClass': return PrivateFieldClass; - } - - throw new Error(`Unknown class: ${loadable.source}`); - } -} - -const classLoader = new MockClassLoader(); - -const parent = new Serializer(); -parent.addSerializer(new ClassSerializer(classLoader)); -parent.addSerializer(new PrimitiveSerializer()); - -const dataClass = new Data(); -dataClass.a = 1; -dataClass.b = true; - -const constructedClass = new Constructed(1, true); -constructedClass.c = 'hello'; - -const nestedClass = new Nested('hello', constructedClass); -const privateClass = new PrivateFieldClass(1, true); - -const serializedDataClass = { serialized: true, name: 'Data', source: 'Data', args: {}, fields: { a: 1, b: true } }; -const serializedConstructedClass = { serialized: true, name: 'Constructed', source: 'Constructed', args: { a: 1, b: true }, fields: { c: 'hello' } }; -const serializedNestedClass = { serialized: true, name: 'Nested', source: 'Nested', args: { name: 'hello', constructed: { serialized: true, name: 'Constructed', source: 'Constructed', args: { a: 1, b: true }, fields: { c: 'hello' } } }, fields: {} }; -const serializedPrivateClass = { serialized: true, name: 'PrivateFieldClass', source: 'PrivateFieldClass', args: { a: undefined, b: undefined, c: undefined }, fields: { } }; - -const serializedInvalidClass = { serialized: true, name: 'Invalid', source: undefined, args: {}, fields: {} }; -const serializedUnserializableClass = { serialized: true, name: 'Infinity', source: undefined, args: {}, fields: {} }; - -const nonObject = 42; -const nonClassObject = new Object(); -const notSerialized = { name: 'Data', source: undefined, args: {}, fields: {} }; -const invalidName = { serialized: true, name: 123, source: undefined, args: {}, fields: {} }; -const invalidArgs = { serialized: true, name: 'Data', source: undefined, args: [], fields: {} }; -const invalidFields = { serialized: true, name: 'Data', source: undefined, args: {}, fields: [] }; - -export { - Data, Constructed, Nested, PrivateFieldClass, - parent, classLoader, - dataClass, constructedClass, nestedClass, privateClass, - serializedDataClass, serializedConstructedClass, serializedNestedClass, serializedPrivateClass, serializedInvalidClass, serializedUnserializableClass, - nonObject, nonClassObject, notSerialized, invalidName, invalidArgs, invalidFields -}; diff --git a/packages/serialization/test/_fixtures/serializers/ObjectSerializer.fixture.ts b/packages/serialization/test/_fixtures/serializers/ObjectSerializer.fixture.ts deleted file mode 100644 index e8bb33e4..00000000 --- a/packages/serialization/test/_fixtures/serializers/ObjectSerializer.fixture.ts +++ /dev/null @@ -1,21 +0,0 @@ - -import Serializer from '../../../src/Serializer'; -import PrimitiveSerializer from '../../../src/serializers/PrimitiveSerializer'; -import ObjectSerializer from '../../../src/serializers/ObjectSerializer'; - -const parent = new Serializer(); -parent.addSerializer(new ObjectSerializer()); -parent.addSerializer(new PrimitiveSerializer()); - -const emptyObject = {}; -const mixedObject = { a: 1, b: true, c: 'hello' }; -const nestedObject = { a: 1, b: true, c: { d: false, e: true } }; - -const nonObject = 42; -const specificObject = new Map(); - -export { - parent, - emptyObject, mixedObject, nestedObject, - nonObject, specificObject -}; diff --git a/packages/serialization/test/_fixtures/serializers/PrimitiveSerializer.fixture.ts b/packages/serialization/test/_fixtures/serializers/PrimitiveSerializer.fixture.ts deleted file mode 100644 index 2e416907..00000000 --- a/packages/serialization/test/_fixtures/serializers/PrimitiveSerializer.fixture.ts +++ /dev/null @@ -1,9 +0,0 @@ - -const numberValue = 1; -const boolValue = true; -const stringValue = 'hello'; -const nullValue = null; -const undefinedValue = undefined; -const objectValue = {}; - -export { numberValue, boolValue, stringValue, nullValue, undefinedValue, objectValue }; diff --git a/packages/serialization/test/fixtures/index.ts b/packages/serialization/test/fixtures/index.ts new file mode 100644 index 00000000..42771205 --- /dev/null +++ b/packages/serialization/test/fixtures/index.ts @@ -0,0 +1,2 @@ + +export * from './serializers.fixture'; diff --git a/packages/serialization/test/_fixtures/Serializer.fixture.ts b/packages/serialization/test/fixtures/serializers.fixture.ts similarity index 81% rename from packages/serialization/test/_fixtures/Serializer.fixture.ts rename to packages/serialization/test/fixtures/serializers.fixture.ts index 9dcdcdb1..d997d45e 100644 --- a/packages/serialization/test/_fixtures/Serializer.fixture.ts +++ b/packages/serialization/test/fixtures/serializers.fixture.ts @@ -1,4 +1,5 @@ +import Serializer from '../../src/Serializer'; import ValueSerializer from '../../src/ValueSerializer'; class FirstSerializer extends ValueSerializer @@ -53,7 +54,16 @@ class StringSerializer extends ValueSerializer async deserialize(value: string): Promise { return value; } } -export { - FirstSerializer, SecondSerializer, - NumberSerializer, StringSerializer +const overrideSerializer = new Serializer(); +overrideSerializer.addSerializer(new FirstSerializer()); +overrideSerializer.addSerializer(new SecondSerializer()); + +const typeSerializer = new Serializer(); +typeSerializer.addSerializer(new NumberSerializer()); +typeSerializer.addSerializer(new StringSerializer()); + +export const SERIALIZERS = +{ + OVERRIDE: overrideSerializer, + TYPE: typeSerializer }; diff --git a/packages/serialization/test/serializers/ArraySerializer.spec.ts b/packages/serialization/test/serializers/ArraySerializer.spec.ts index 5951514c..660b256b 100644 --- a/packages/serialization/test/serializers/ArraySerializer.spec.ts +++ b/packages/serialization/test/serializers/ArraySerializer.spec.ts @@ -1,16 +1,17 @@ import { describe, expect, it } from 'vitest'; +import Serializer from '../../src/Serializer'; +import PrimitiveSerializer from '../../src/serializers/PrimitiveSerializer'; import ArraySerializer from '../../src/serializers/ArraySerializer'; -import { - parent, - emptyArray, mixedArray, nestedArray, - nonObject, nonArray -} from '../_fixtures/serializers/ArraySerializer.fixture'; +import { ARRAYS } from './fixtures'; const serializer = new ArraySerializer(); -serializer.parent = parent; +const parent = new Serializer(); + +parent.addSerializer(serializer); +parent.addSerializer(new PrimitiveSerializer()); describe('serializers/ArraySerializer', () => { @@ -18,15 +19,15 @@ describe('serializers/ArraySerializer', () => { it('should tell it can serialize an array', () => { - const supportsArray = serializer.canSerialize(emptyArray); + const supportsArray = serializer.canSerialize(ARRAYS.EMPTY); expect(supportsArray).toBeTruthy(); }); it('should tell it cannot serialize others', () => { - const supportsNonObject = serializer.canSerialize(nonObject); - const supportsNonArray = serializer.canSerialize(nonArray); + const supportsNonObject = serializer.canSerialize(ARRAYS.NON_OBJECT); + const supportsNonArray = serializer.canSerialize(ARRAYS.NON_ARRAY); expect(supportsNonObject).toBeFalsy(); expect(supportsNonArray).toBeFalsy(); @@ -37,15 +38,15 @@ describe('serializers/ArraySerializer', () => { it('should tell it can deserialize an array', () => { - const supportsArray = serializer.canDeserialize(emptyArray); + const supportsArray = serializer.canDeserialize(ARRAYS.EMPTY); expect(supportsArray).toBeTruthy(); }); it('should tell it cannot deserialize others', () => { - const supportsNonObject = serializer.canDeserialize(nonObject); - const supportsNonArray = serializer.canDeserialize(nonArray); + const supportsNonObject = serializer.canDeserialize(ARRAYS.NON_OBJECT); + const supportsNonArray = serializer.canDeserialize(ARRAYS.NON_ARRAY); expect(supportsNonObject).toBeFalsy(); expect(supportsNonArray).toBeFalsy(); @@ -56,13 +57,13 @@ describe('serializers/ArraySerializer', () => { it('should serialize an array', async () => { - const resultEmptyArray = await serializer.serialize(emptyArray); - const resultMixedArray = await serializer.serialize(mixedArray); - const resultNestedArray = await serializer.serialize(nestedArray); + const resultEmptyArray = await serializer.serialize(ARRAYS.EMPTY); + const resultMixedArray = await serializer.serialize(ARRAYS.MIXED); + const resultNestedArray = await serializer.serialize(ARRAYS.NESTED); - expect(resultEmptyArray).toStrictEqual(emptyArray); - expect(resultMixedArray).toStrictEqual(mixedArray); - expect(resultNestedArray).toStrictEqual(nestedArray); + expect(resultEmptyArray).toStrictEqual(ARRAYS.EMPTY); + expect(resultMixedArray).toStrictEqual(ARRAYS.MIXED); + expect(resultNestedArray).toStrictEqual(ARRAYS.NESTED); }); }); @@ -70,13 +71,13 @@ describe('serializers/ArraySerializer', () => { it('should deserialize an array', async () => { - const resultEmptyArray = await serializer.deserialize(emptyArray); - const resultMixedArray = await serializer.deserialize(mixedArray); - const resultNestedArray = await serializer.deserialize(nestedArray); + const resultEmptyArray = await serializer.deserialize(ARRAYS.EMPTY); + const resultMixedArray = await serializer.deserialize(ARRAYS.MIXED); + const resultNestedArray = await serializer.deserialize(ARRAYS.NESTED); - expect(resultEmptyArray).toStrictEqual(emptyArray); - expect(resultMixedArray).toStrictEqual(mixedArray); - expect(resultNestedArray).toStrictEqual(nestedArray); + expect(resultEmptyArray).toStrictEqual(ARRAYS.EMPTY); + expect(resultMixedArray).toStrictEqual(ARRAYS.MIXED); + expect(resultNestedArray).toStrictEqual(ARRAYS.NESTED); }); }); }); diff --git a/packages/serialization/test/serializers/BigIntSerializer.spec.ts b/packages/serialization/test/serializers/BigIntSerializer.spec.ts index ccacc5aa..727fad38 100644 --- a/packages/serialization/test/serializers/BigIntSerializer.spec.ts +++ b/packages/serialization/test/serializers/BigIntSerializer.spec.ts @@ -4,12 +4,7 @@ import { describe, expect, it } from 'vitest'; import BigIntSerializer from '../../src/serializers/BigIntSerializer'; import InvalidBigIntString from '../../src/serializers/errors/InvalidBigIntString'; -import -{ - validBigInt, - serializedValidBigInt, - nonObject, nonBigInt, notSerialized, invalidName, invalidBigIntValue, invalidBigIntString -} from '../_fixtures/serializers/BigIntSerializer.fixture'; +import { BIG_INTEGERS } from './fixtures'; const serializer = new BigIntSerializer(); @@ -19,15 +14,15 @@ describe('serializers/BigIntSerializer', () => { it('should tell it can serialize a big int', () => { - const supportsBigInt = serializer.canSerialize(validBigInt); + const supportsBigInt = serializer.canSerialize(BIG_INTEGERS.VALID); expect(supportsBigInt).toBeTruthy(); }); it('should tell it cannot serialize others', () => { - const supportsNonObject = serializer.canSerialize(nonObject); - const supportsNonBigInt = serializer.canSerialize(nonBigInt); + const supportsNonObject = serializer.canSerialize(BIG_INTEGERS.NON_OBJECT); + const supportsNonBigInt = serializer.canSerialize(BIG_INTEGERS.NON_BIGINT); expect(supportsNonObject).toBeFalsy(); expect(supportsNonBigInt).toBeFalsy(); @@ -38,17 +33,17 @@ describe('serializers/BigIntSerializer', () => { it('should tell it can deserialize a big int', () => { - const supportsBigInt = serializer.canDeserialize(serializedValidBigInt); + const supportsBigInt = serializer.canDeserialize(BIG_INTEGERS.VALID_SERIALIZED); expect(supportsBigInt).toBeTruthy(); }); it('should tell it cannot deserialize others', () => { - const supportsNonObject = serializer.canDeserialize(nonObject); - const supportsNotSerialized = serializer.canDeserialize(notSerialized); - const supportsInvalidName = serializer.canDeserialize(invalidName); - const supportsInvalidBigIntValue = serializer.canDeserialize(invalidBigIntValue); + const supportsNonObject = serializer.canDeserialize(BIG_INTEGERS.NON_OBJECT); + const supportsNotSerialized = serializer.canDeserialize(BIG_INTEGERS.NOT_SERIALIZED); + const supportsInvalidName = serializer.canDeserialize(BIG_INTEGERS.INVALID_NAME); + const supportsInvalidBigIntValue = serializer.canDeserialize(BIG_INTEGERS.INVALID_BIGINT_VALUE); expect(supportsNonObject).toBeFalsy(); expect(supportsNotSerialized).toBeFalsy(); @@ -61,9 +56,9 @@ describe('serializers/BigIntSerializer', () => { it('should serialize a big int', async () => { - const resultValidBigInt = await serializer.serialize(validBigInt); + const resultValidBigInt = await serializer.serialize(BIG_INTEGERS.VALID); - expect(resultValidBigInt).toStrictEqual(serializedValidBigInt); + expect(resultValidBigInt).toStrictEqual(BIG_INTEGERS.VALID_SERIALIZED); }); }); @@ -71,14 +66,14 @@ describe('serializers/BigIntSerializer', () => { it('should deserialize a big int', async () => { - const resultValidBigInt = await serializer.deserialize(serializedValidBigInt); + const resultValidBigInt = await serializer.deserialize(BIG_INTEGERS.VALID_SERIALIZED); - expect(resultValidBigInt).toStrictEqual(validBigInt); + expect(resultValidBigInt).toStrictEqual(BIG_INTEGERS.VALID); }); it('should not deserialize a big int with an invalid big int string', async () => { - const deserialize = async () => serializer.deserialize(invalidBigIntString); + const deserialize = async () => serializer.deserialize(BIG_INTEGERS.INVALID_BIGINT_STRING); expect(deserialize).rejects.toStrictEqual(new InvalidBigIntString('1.3')); }); diff --git a/packages/serialization/test/serializers/ClassSerializer.spec.ts b/packages/serialization/test/serializers/ClassSerializer.spec.ts index 1b48d6bd..18319e90 100644 --- a/packages/serialization/test/serializers/ClassSerializer.spec.ts +++ b/packages/serialization/test/serializers/ClassSerializer.spec.ts @@ -4,18 +4,17 @@ import { describe, expect, it } from 'vitest'; import ClassNotFound from '../../src/errors/ClassNotFound'; import InvalidClass from '../../src/errors/InvalidClass'; +import Serializer from '../../src/Serializer'; +import PrimitiveSerializer from '../../src/serializers/PrimitiveSerializer'; import ClassSerializer from '../../src/serializers/ClassSerializer'; -import - { - parent, classLoader, - dataClass, constructedClass, nestedClass, privateClass, - serializedDataClass, serializedConstructedClass, serializedNestedClass, serializedPrivateClass, serializedInvalidClass, serializedUnserializableClass, - nonObject, nonClassObject, notSerialized, invalidName, invalidArgs, invalidFields - } from '../_fixtures/serializers/ClassSerializer.fixture'; +import { CLASSES, classResolver } from './fixtures'; + +const serializer = new ClassSerializer(classResolver); +const parent = new Serializer(); -const serializer = new ClassSerializer(classLoader); -serializer.parent = parent; +parent.addSerializer(serializer); +parent.addSerializer(new PrimitiveSerializer()); describe('serializers/ClassSerializer', () => { @@ -23,15 +22,15 @@ describe('serializers/ClassSerializer', () => { it('should tell it can serialize a class instance', () => { - const supportsClass = serializer.canSerialize(dataClass); + const supportsClass = serializer.canSerialize(CLASSES.DATA_INSTANCE); expect(supportsClass).toBeTruthy(); }); it('should tell it cannot serialize others', () => { - const supportsNonObject = serializer.canSerialize(nonObject); - const supportsNonClassObject = serializer.canSerialize(nonClassObject); + const supportsNonObject = serializer.canSerialize(CLASSES.NON_OBJECT); + const supportsNonClassObject = serializer.canSerialize(CLASSES.NON_CLASS_OBJECT); expect(supportsNonObject).toBeFalsy(); expect(supportsNonClassObject).toBeFalsy(); @@ -42,20 +41,20 @@ describe('serializers/ClassSerializer', () => { it('should tell it can deserialize a class instance', () => { - const supportsClass = serializer.canDeserialize(serializedDataClass); + const supportsClass = serializer.canDeserialize(CLASSES.DATA_SERIALIZED); expect(supportsClass).toBeTruthy(); }); it('should tell it cannot deserialize others', () => { - const supportsNotSerialized = serializer.canDeserialize(notSerialized); - const supportsInvalidName = serializer.canDeserialize(invalidName); - const supportsInvalidArgs = serializer.canDeserialize(invalidArgs); - const supportsInvalidFields = serializer.canDeserialize(invalidFields); + const supportsNotSerialized = serializer.canDeserialize(CLASSES.NOT_SERIALIZED); + const supportsInvalidKey = serializer.canDeserialize(CLASSES.INVALID_KEY); + const supportsInvalidArgs = serializer.canDeserialize(CLASSES.INVALID_ARGS); + const supportsInvalidFields = serializer.canDeserialize(CLASSES.INVALID_FIELDS); expect(supportsNotSerialized).toBeFalsy(); - expect(supportsInvalidName).toBeFalsy(); + expect(supportsInvalidKey).toBeFalsy(); expect(supportsInvalidArgs).toBeFalsy(); expect(supportsInvalidFields).toBeFalsy(); }); @@ -65,20 +64,20 @@ describe('serializers/ClassSerializer', () => { it('should serialize a class instance', async () => { - const resultDataClass = await serializer.serialize(dataClass); - const resultConstructedClass = await serializer.serialize(constructedClass); - const resultNestedClass = await serializer.serialize(nestedClass); + const resultDataClass = await serializer.serialize(CLASSES.DATA_INSTANCE); + const resultConstructedClass = await serializer.serialize(CLASSES.CONSTRUCTED_INSTANCE); + const resultNestedClass = await serializer.serialize(CLASSES.NESTED_INSTANCE); - expect(resultDataClass).toStrictEqual(serializedDataClass); - expect(resultConstructedClass).toStrictEqual(serializedConstructedClass); - expect(resultNestedClass).toStrictEqual(serializedNestedClass); + expect(resultDataClass).toStrictEqual(CLASSES.DATA_SERIALIZED); + expect(resultConstructedClass).toStrictEqual(CLASSES.CONSTRUCTED_SERIALIZED); + expect(resultNestedClass).toStrictEqual(CLASSES.NESTED_SERIALIZED); }); it('should not serialize set or get only variables', async () => { - const resultPrivateClass = await serializer.serialize(privateClass); + const resultPrivateClass = await serializer.serialize(CLASSES.PRIVATE_INSTANCE); - expect(resultPrivateClass).toStrictEqual(serializedPrivateClass); + expect(resultPrivateClass).toStrictEqual(CLASSES.PRIVATE_SERIALIZED); }); }); @@ -86,25 +85,25 @@ describe('serializers/ClassSerializer', () => { it('should deserialize a class instance', async () => { - const resultDataClass = await serializer.deserialize(serializedDataClass); - const resultConstructedClass = await serializer.deserialize(serializedConstructedClass); - const resultNestedClass = await serializer.deserialize(serializedNestedClass); + const resultDataClass = await serializer.deserialize(CLASSES.DATA_SERIALIZED); + const resultConstructedClass = await serializer.deserialize(CLASSES.CONSTRUCTED_SERIALIZED); + const resultNestedClass = await serializer.deserialize(CLASSES.NESTED_SERIALIZED); - expect(resultDataClass).toStrictEqual(dataClass); - expect(resultConstructedClass).toStrictEqual(constructedClass); - expect(resultNestedClass).toStrictEqual(nestedClass); + expect(resultDataClass).toStrictEqual(CLASSES.DATA_INSTANCE); + expect(resultConstructedClass).toStrictEqual(CLASSES.CONSTRUCTED_INSTANCE); + expect(resultNestedClass).toStrictEqual(CLASSES.NESTED_INSTANCE); }); it('should not deserialize invalid objects', async () => { - const deserialize = async () => serializer.deserialize(serializedInvalidClass); + const deserialize = async () => serializer.deserialize(CLASSES.INVALID_SERIALIZED); expect(deserialize).rejects.toStrictEqual(new ClassNotFound('Invalid')); }); it('should not deserialize non-function instances', async () => { - const deserialize = async () => serializer.deserialize(serializedUnserializableClass); + const deserialize = async () => serializer.deserialize(CLASSES.UNSERIALIZABLE); expect(deserialize).rejects.toStrictEqual(new InvalidClass('Infinity')); }); diff --git a/packages/serialization/test/serializers/DateSerializer.spec.ts b/packages/serialization/test/serializers/DateSerializer.spec.ts index c7f68c11..8d8e6740 100644 --- a/packages/serialization/test/serializers/DateSerializer.spec.ts +++ b/packages/serialization/test/serializers/DateSerializer.spec.ts @@ -4,12 +4,7 @@ import { describe, expect, it } from 'vitest'; import DateSerializer from '../../src/serializers/DateSerializer'; import InvalidDateString from '../../src/serializers/errors/InvalidDateString'; -import - { - fixedDate, - serializedFixedDate, - nonObject, nonDate, notSerialized, invalidName, invalidDateValue, invalidDateString - } from '../_fixtures/serializers/DateSerializer.fixture'; +import { DATES } from './fixtures'; const serializer = new DateSerializer(); @@ -19,15 +14,15 @@ describe('serializers/DateSerializer', () => { it('should tell it can serialize a date', () => { - const supportsDate = serializer.canSerialize(fixedDate); + const supportsDate = serializer.canSerialize(DATES.FIXED); expect(supportsDate).toBeTruthy(); }); it('should tell it cannot serialize others', () => { - const supportsNonObject = serializer.canSerialize(nonObject); - const supportsNonDate = serializer.canSerialize(nonDate); + const supportsNonObject = serializer.canSerialize(DATES.NON_OBJECT); + const supportsNonDate = serializer.canSerialize(DATES.NON_DATE); expect(supportsNonObject).toBeFalsy(); expect(supportsNonDate).toBeFalsy(); @@ -38,17 +33,17 @@ describe('serializers/DateSerializer', () => { it('should tell it can deserialize a date', () => { - const supportsDate = serializer.canDeserialize(serializedFixedDate); + const supportsDate = serializer.canDeserialize(DATES.FIXED_SERIALIZED); expect(supportsDate).toBeTruthy(); }); it('should tell it cannot deserialize others', () => { - const supportsNonObject = serializer.canDeserialize(nonObject); - const supportsNotSerialized = serializer.canDeserialize(notSerialized); - const supportsInvalidName = serializer.canDeserialize(invalidName); - const supportsInvalidDateValue = serializer.canDeserialize(invalidDateValue); + const supportsNonObject = serializer.canDeserialize(DATES.NON_OBJECT); + const supportsNotSerialized = serializer.canDeserialize(DATES.NOT_SERIALIZED); + const supportsInvalidName = serializer.canDeserialize(DATES.INVALID_NAME); + const supportsInvalidDateValue = serializer.canDeserialize(DATES.INVALID_DATE_VALUE); expect(supportsNonObject).toBeFalsy(); expect(supportsNotSerialized).toBeFalsy(); @@ -61,9 +56,9 @@ describe('serializers/DateSerializer', () => { it('should serialize a date', async () => { - const resultFixedDate = await serializer.serialize(fixedDate); + const resultFixedDate = await serializer.serialize(DATES.FIXED); - expect(resultFixedDate).toStrictEqual(serializedFixedDate); + expect(resultFixedDate).toStrictEqual(DATES.FIXED_SERIALIZED); }); }); @@ -71,14 +66,14 @@ describe('serializers/DateSerializer', () => { it('should deserialize a date', async () => { - const resultFixedDate = await serializer.deserialize(serializedFixedDate); + const resultFixedDate = await serializer.deserialize(DATES.FIXED_SERIALIZED); - expect(resultFixedDate).toStrictEqual(fixedDate); + expect(resultFixedDate).toStrictEqual(DATES.FIXED); }); it('should not deserialize a date with an invalid date string', async () => { - const deserialize = async () => serializer.deserialize(invalidDateString); + const deserialize = async () => serializer.deserialize(DATES.INVALID_DATE_STRING); expect(deserialize).rejects.toStrictEqual(new InvalidDateString('hello')); }); diff --git a/packages/serialization/test/serializers/ErrorSerializer.spec.ts b/packages/serialization/test/serializers/ErrorSerializer.spec.ts index 68a0449b..f2fe86ed 100644 --- a/packages/serialization/test/serializers/ErrorSerializer.spec.ts +++ b/packages/serialization/test/serializers/ErrorSerializer.spec.ts @@ -3,12 +3,7 @@ import { describe, expect, it } from 'vitest'; import ErrorSerializer from '../../src/serializers/ErrorSerializer'; -import { - plainError, evalError, rangeError, referenceError, syntaxError, typeError, uriError, - serializedPlainError, serializedEvalError, serializedRangeError, serializedReferenceError, serializedSyntaxError, serializedTypeError, serializedURIError, - customError, otherClass, - notSerialized, invalidName, invalidType -} from '../_fixtures/serializers/ErrorSerializer.fixture'; +import { ERRORS } from './fixtures'; const serializer = new ErrorSerializer(); @@ -18,13 +13,13 @@ describe('serializers/ErrorSerializer', () => { it('should tell it can serialize an error', () => { - const supportsPlainError = serializer.canSerialize(plainError); - const supportsEvalError = serializer.canSerialize(evalError); - const supportsRangeError = serializer.canSerialize(rangeError); - const supportsReferenceError = serializer.canSerialize(referenceError); - const supportsSyntaxError = serializer.canSerialize(syntaxError); - const supportsTypeError = serializer.canSerialize(typeError); - const supportsURIError = serializer.canSerialize(uriError); + const supportsPlainError = serializer.canSerialize(ERRORS.PLAIN); + const supportsEvalError = serializer.canSerialize(ERRORS.EVAL); + const supportsRangeError = serializer.canSerialize(ERRORS.RANGE); + const supportsReferenceError = serializer.canSerialize(ERRORS.REFERENCE); + const supportsSyntaxError = serializer.canSerialize(ERRORS.SYNTAX); + const supportsTypeError = serializer.canSerialize(ERRORS.TYPE); + const supportsURIError = serializer.canSerialize(ERRORS.URI); expect(supportsPlainError).toBeTruthy(); expect(supportsEvalError).toBeTruthy(); @@ -37,11 +32,11 @@ describe('serializers/ErrorSerializer', () => it('should tell it cannot serialize others', () => { - const supportsCustomError = serializer.canSerialize(customError); - const supportsOtherClass = serializer.canSerialize(otherClass); - const supportsNotSerialized = serializer.canDeserialize(notSerialized); - const supportsInvalidName = serializer.canDeserialize(invalidName); - const supportsInvalidType = serializer.canDeserialize(invalidType); + const supportsCustomError = serializer.canSerialize(ERRORS.CUSTOM); + const supportsOtherClass = serializer.canSerialize(ERRORS.OTHER_CLASS); + const supportsNotSerialized = serializer.canDeserialize(ERRORS.NOT_SERIALIZED); + const supportsInvalidName = serializer.canDeserialize(ERRORS.INVALID_NAME); + const supportsInvalidType = serializer.canDeserialize(ERRORS.INVALID_TYPE); expect(supportsCustomError).toBeFalsy(); expect(supportsOtherClass).toBeFalsy(); @@ -55,21 +50,21 @@ describe('serializers/ErrorSerializer', () => { it('should serialize an error', async () => { - const resultPlainError = await serializer.serialize(plainError); - const resultEvalError = await serializer.serialize(evalError); - const resultRangeError = await serializer.serialize(rangeError); - const resultReferenceError = await serializer.serialize(referenceError); - const resultSyntaxError = await serializer.serialize(syntaxError); - const resultTypeError = await serializer.serialize(typeError); - const resultURIError = await serializer.serialize(uriError); + const resultPlainError = await serializer.serialize(ERRORS.PLAIN); + const resultEvalError = await serializer.serialize(ERRORS.EVAL); + const resultRangeError = await serializer.serialize(ERRORS.RANGE); + const resultReferenceError = await serializer.serialize(ERRORS.REFERENCE); + const resultSyntaxError = await serializer.serialize(ERRORS.SYNTAX); + const resultTypeError = await serializer.serialize(ERRORS.TYPE); + const resultURIError = await serializer.serialize(ERRORS.URI); - expect(resultPlainError).toStrictEqual(serializedPlainError); - expect(resultEvalError).toStrictEqual(serializedEvalError); - expect(resultRangeError).toStrictEqual(serializedRangeError); - expect(resultReferenceError).toStrictEqual(serializedReferenceError); - expect(resultSyntaxError).toStrictEqual(serializedSyntaxError); - expect(resultTypeError).toStrictEqual(serializedTypeError); - expect(resultURIError).toStrictEqual(serializedURIError); + expect(resultPlainError).toStrictEqual(ERRORS.PLAIN_SERIALIZED); + expect(resultEvalError).toStrictEqual(ERRORS.EVAL_SERIALIZED); + expect(resultRangeError).toStrictEqual(ERRORS.RANGE_SERIALIZED); + expect(resultReferenceError).toStrictEqual(ERRORS.REFERENCE_SERIALIZED); + expect(resultSyntaxError).toStrictEqual(ERRORS.SYNTAX_SERIALIZED); + expect(resultTypeError).toStrictEqual(ERRORS.TYPE_SERIALIZED); + expect(resultURIError).toStrictEqual(ERRORS.URI_SERIALIZED); }); }); @@ -77,21 +72,21 @@ describe('serializers/ErrorSerializer', () => { it('should deserialize an error', async () => { - const resultPlainError = await serializer.deserialize(serializedPlainError); - const resultEvalError = await serializer.deserialize(serializedEvalError); - const resultRangeError = await serializer.deserialize(serializedRangeError); - const resultReferenceError = await serializer.deserialize(serializedReferenceError); - const resultSyntaxError = await serializer.deserialize(serializedSyntaxError); - const resultTypeError = await serializer.deserialize(serializedTypeError); - const resultURIError = await serializer.deserialize(serializedURIError); + const resultPlainError = await serializer.deserialize(ERRORS.PLAIN_SERIALIZED); + const resultEvalError = await serializer.deserialize(ERRORS.EVAL_SERIALIZED); + const resultRangeError = await serializer.deserialize(ERRORS.RANGE_SERIALIZED); + const resultReferenceError = await serializer.deserialize(ERRORS.REFERENCE_SERIALIZED); + const resultSyntaxError = await serializer.deserialize(ERRORS.SYNTAX_SERIALIZED); + const resultTypeError = await serializer.deserialize(ERRORS.TYPE_SERIALIZED); + const resultURIError = await serializer.deserialize(ERRORS.URI_SERIALIZED); - expect(resultPlainError).toStrictEqual(plainError); - expect(resultEvalError).toStrictEqual(evalError); - expect(resultRangeError).toStrictEqual(rangeError); - expect(resultReferenceError).toStrictEqual(referenceError); - expect(resultSyntaxError).toStrictEqual(syntaxError); - expect(resultTypeError).toStrictEqual(typeError); - expect(resultURIError).toStrictEqual(uriError); + expect(resultPlainError).toStrictEqual(ERRORS.PLAIN); + expect(resultEvalError).toStrictEqual(ERRORS.EVAL); + expect(resultRangeError).toStrictEqual(ERRORS.RANGE); + expect(resultReferenceError).toStrictEqual(ERRORS.REFERENCE); + expect(resultSyntaxError).toStrictEqual(ERRORS.SYNTAX); + expect(resultTypeError).toStrictEqual(ERRORS.TYPE); + expect(resultURIError).toStrictEqual(ERRORS.URI); }); }); }); diff --git a/packages/serialization/test/serializers/MapSerializer.spec.ts b/packages/serialization/test/serializers/MapSerializer.spec.ts index ecabb352..c207abc0 100644 --- a/packages/serialization/test/serializers/MapSerializer.spec.ts +++ b/packages/serialization/test/serializers/MapSerializer.spec.ts @@ -1,17 +1,17 @@ import { describe, expect, it } from 'vitest'; +import Serializer from '../../src/Serializer'; +import PrimitiveSerializer from '../../src/serializers/PrimitiveSerializer'; import MapSerializer from '../../src/serializers/MapSerializer'; -import { - parent, - emptyMap, mixedMap, nestedMap, - serializedEmptyMap, serializedMixedMap, serializedNestedMap, - nonObject, nonMap, notSerialized, invalidName, invalidKeys, invalidValues -} from '../_fixtures/serializers/MapSerializer.fixture'; +import { MAPS } from './fixtures'; const serializer = new MapSerializer(); -serializer.parent = parent; +const parent = new Serializer(); + +parent.addSerializer(serializer); +parent.addSerializer(new PrimitiveSerializer()); describe('serializers/MapSerializer', () => { @@ -19,15 +19,15 @@ describe('serializers/MapSerializer', () => { it('should tell it can serialize a map', () => { - const supportsMap = serializer.canSerialize(emptyMap); + const supportsMap = serializer.canSerialize(MAPS.EMPTY); expect(supportsMap).toBeTruthy(); }); it('should tell it cannot serialize others', () => { - const supportsNonObject = serializer.canSerialize(nonObject); - const supportsNonMap = serializer.canSerialize(nonMap); + const supportsNonObject = serializer.canSerialize(MAPS.NON_OBJECT); + const supportsNonMap = serializer.canSerialize(MAPS.NON_MAP); expect(supportsNonObject).toBeFalsy(); expect(supportsNonMap).toBeFalsy(); @@ -38,18 +38,18 @@ describe('serializers/MapSerializer', () => { it('should tell it can deserialize a map', () => { - const supportsMap = serializer.canDeserialize(serializedEmptyMap); + const supportsMap = serializer.canDeserialize(MAPS.EMPTY_SERIALIZED); expect(supportsMap).toBeTruthy(); }); it('should tell it cannot deserialize others', () => { - const supportsNonObject = serializer.canDeserialize(nonObject); - const supportsNotSerialized = serializer.canDeserialize(notSerialized); - const supportsInvalidName = serializer.canDeserialize(invalidName); - const supportsInvalidKeys = serializer.canDeserialize(invalidKeys); - const supportsInvalidValues = serializer.canDeserialize(invalidValues); + const supportsNonObject = serializer.canDeserialize(MAPS.NON_OBJECT); + const supportsNotSerialized = serializer.canDeserialize(MAPS.NOT_SERIALIZED); + const supportsInvalidName = serializer.canDeserialize(MAPS.INVALID_NAME); + const supportsInvalidKeys = serializer.canDeserialize(MAPS.INVALID_KEYS); + const supportsInvalidValues = serializer.canDeserialize(MAPS.INVALID_VALUES); expect(supportsNonObject).toBeFalsy(); expect(supportsNotSerialized).toBeFalsy(); @@ -63,13 +63,13 @@ describe('serializers/MapSerializer', () => { it('should serialize a map', async () => { - const resultEmptyMap = await serializer.serialize(emptyMap); - const resultMixedMap = await serializer.serialize(mixedMap); - const resultNestedMap = await serializer.serialize(nestedMap); + const resultEmptyMap = await serializer.serialize(MAPS.EMPTY); + const resultMixedMap = await serializer.serialize(MAPS.MIXED); + const resultNestedMap = await serializer.serialize(MAPS.NESTED); - expect(resultEmptyMap).toStrictEqual(serializedEmptyMap); - expect(resultMixedMap).toStrictEqual(serializedMixedMap); - expect(resultNestedMap).toStrictEqual(serializedNestedMap); + expect(resultEmptyMap).toStrictEqual(MAPS.EMPTY_SERIALIZED); + expect(resultMixedMap).toStrictEqual(MAPS.MIXED_SERIALIZED); + expect(resultNestedMap).toStrictEqual(MAPS.NESTED_SERIALIZED); }); }); @@ -77,13 +77,13 @@ describe('serializers/MapSerializer', () => { it('should deserialize a map', async () => { - const resultEmptyMap = await serializer.deserialize(serializedEmptyMap); - const resultMixedMap = await serializer.deserialize(serializedMixedMap); - const resultNestedMap = await serializer.deserialize(serializedNestedMap); + const resultEmptyMap = await serializer.deserialize(MAPS.EMPTY_SERIALIZED); + const resultMixedMap = await serializer.deserialize(MAPS.MIXED_SERIALIZED); + const resultNestedMap = await serializer.deserialize(MAPS.NESTED_SERIALIZED); - expect(resultEmptyMap).toStrictEqual(emptyMap); - expect(resultMixedMap).toStrictEqual(mixedMap); - expect(resultNestedMap).toStrictEqual(nestedMap); + expect(resultEmptyMap).toStrictEqual(MAPS.EMPTY); + expect(resultMixedMap).toStrictEqual(MAPS.MIXED); + expect(resultNestedMap).toStrictEqual(MAPS.NESTED); }); }); }); diff --git a/packages/serialization/test/serializers/ObjectSerializer.spec.ts b/packages/serialization/test/serializers/ObjectSerializer.spec.ts index dd7e6118..739eb165 100644 --- a/packages/serialization/test/serializers/ObjectSerializer.spec.ts +++ b/packages/serialization/test/serializers/ObjectSerializer.spec.ts @@ -1,16 +1,17 @@ import { describe, expect, it } from 'vitest'; +import Serializer from '../../src/Serializer'; +import PrimitiveSerializer from '../../src/serializers/PrimitiveSerializer'; import ObjectSerializer from '../../src/serializers/ObjectSerializer'; -import { - parent, - emptyObject, mixedObject, nestedObject, - nonObject, specificObject -} from '../_fixtures/serializers/ObjectSerializer.fixture'; +import { OBJECTS } from './fixtures'; const serializer = new ObjectSerializer(); -serializer.parent = parent; +const parent = new Serializer(); + +parent.addSerializer(serializer); +parent.addSerializer(new PrimitiveSerializer()); describe('serializers/ObjectSerializer', () => { @@ -18,15 +19,15 @@ describe('serializers/ObjectSerializer', () => { it('should tell it can serialize an object', () => { - const supportsObject = serializer.canSerialize(emptyObject); + const supportsObject = serializer.canSerialize(OBJECTS.EMPTY); expect(supportsObject).toBeTruthy(); }); it('should tell it cannot serialize others', () => { - const supportsNonObject = serializer.canSerialize(nonObject); - const supportsSpecificObject = serializer.canSerialize(specificObject); + const supportsNonObject = serializer.canSerialize(OBJECTS.NON_OBJECT); + const supportsSpecificObject = serializer.canSerialize(OBJECTS.SPECIFIC_OBJECT); expect(supportsNonObject).toBeFalsy(); expect(supportsSpecificObject).toBeFalsy(); @@ -37,15 +38,15 @@ describe('serializers/ObjectSerializer', () => { it('should tell it can deserialize an object', () => { - const supportsObject = serializer.canDeserialize(emptyObject); + const supportsObject = serializer.canDeserialize(OBJECTS.EMPTY); expect(supportsObject).toBeTruthy(); }); it('should tell it cannot deserialize others', () => { - const supportsNonObject = serializer.canDeserialize(nonObject); - const supportsSpecificObject = serializer.canDeserialize(specificObject); + const supportsNonObject = serializer.canDeserialize(OBJECTS.NON_OBJECT); + const supportsSpecificObject = serializer.canDeserialize(OBJECTS.SPECIFIC_OBJECT); expect(supportsNonObject).toBeFalsy(); expect(supportsSpecificObject).toBeFalsy(); @@ -56,13 +57,13 @@ describe('serializers/ObjectSerializer', () => { it('should serialize an object', async () => { - const resultEmptyObject = await serializer.serialize(emptyObject); - const resultMixedObject = await serializer.serialize(mixedObject); - const resultNestedObject = await serializer.serialize(nestedObject); + const resultEmptyObject = await serializer.serialize(OBJECTS.EMPTY); + const resultMixedObject = await serializer.serialize(OBJECTS.MIXED); + const resultNestedObject = await serializer.serialize(OBJECTS.NESTED); - expect(resultEmptyObject).toStrictEqual(emptyObject); - expect(resultMixedObject).toStrictEqual(mixedObject); - expect(resultNestedObject).toStrictEqual(nestedObject); + expect(resultEmptyObject).toStrictEqual(OBJECTS.EMPTY); + expect(resultMixedObject).toStrictEqual(OBJECTS.MIXED); + expect(resultNestedObject).toStrictEqual(OBJECTS.NESTED); }); }); @@ -70,13 +71,13 @@ describe('serializers/ObjectSerializer', () => { it('should deserialize an object', async () => { - const resultEmptyObject = await serializer.deserialize(emptyObject); - const resultMixedObject = await serializer.deserialize(mixedObject); - const resultNestedObject = await serializer.deserialize(nestedObject); + const resultEmptyObject = await serializer.deserialize(OBJECTS.EMPTY); + const resultMixedObject = await serializer.deserialize(OBJECTS.MIXED); + const resultNestedObject = await serializer.deserialize(OBJECTS.NESTED); - expect(resultEmptyObject).toStrictEqual(emptyObject); - expect(resultMixedObject).toStrictEqual(mixedObject); - expect(resultNestedObject).toStrictEqual(nestedObject); + expect(resultEmptyObject).toStrictEqual(OBJECTS.EMPTY); + expect(resultMixedObject).toStrictEqual(OBJECTS.MIXED); + expect(resultNestedObject).toStrictEqual(OBJECTS.NESTED); }); }); }); diff --git a/packages/serialization/test/serializers/PrimitiveSerializer.spec.ts b/packages/serialization/test/serializers/PrimitiveSerializer.spec.ts index 07ae4b14..b6e6a800 100644 --- a/packages/serialization/test/serializers/PrimitiveSerializer.spec.ts +++ b/packages/serialization/test/serializers/PrimitiveSerializer.spec.ts @@ -3,9 +3,7 @@ import { describe, expect, it } from 'vitest'; import PrimitiveSerializer from '../../src/serializers/PrimitiveSerializer'; -import { - numberValue, boolValue, stringValue, nullValue, undefinedValue, objectValue -} from '../_fixtures/serializers/PrimitiveSerializer.fixture'; +import { PRIMITIVES } from './fixtures'; const serializer = new PrimitiveSerializer(); @@ -15,11 +13,11 @@ describe('serializers/PrimitiveSerializer', () => { it('should tell it can serialize a primitive', () => { - const supportsNumber = serializer.canSerialize(numberValue); - const supportsBool = serializer.canSerialize(boolValue); - const supportsString = serializer.canSerialize(stringValue); - const supportsNull = serializer.canSerialize(nullValue); - const supportsUndefined = serializer.canSerialize(undefinedValue); + const supportsNumber = serializer.canSerialize(PRIMITIVES.NUMBER); + const supportsBool = serializer.canSerialize(PRIMITIVES.BOOL); + const supportsString = serializer.canSerialize(PRIMITIVES.STRING); + const supportsNull = serializer.canSerialize(PRIMITIVES.NULL); + const supportsUndefined = serializer.canSerialize(PRIMITIVES.UNDEFINED); expect(supportsNumber).toBeTruthy(); expect(supportsBool).toBeTruthy(); @@ -30,7 +28,7 @@ describe('serializers/PrimitiveSerializer', () => it('should tell it cannot serialize others', () => { - const supportsObject = serializer.canSerialize(objectValue); + const supportsObject = serializer.canSerialize(PRIMITIVES.OBJECT); expect(supportsObject).toBeFalsy(); }); @@ -40,11 +38,11 @@ describe('serializers/PrimitiveSerializer', () => { it('should tell it can deserialize a primitive', () => { - const supportsNumber = serializer.canSerialize(numberValue); - const supportsBool = serializer.canSerialize(boolValue); - const supportsString = serializer.canSerialize(stringValue); - const supportsNull = serializer.canSerialize(nullValue); - const supportsUndefined = serializer.canSerialize(undefinedValue); + const supportsNumber = serializer.canSerialize(PRIMITIVES.NUMBER); + const supportsBool = serializer.canSerialize(PRIMITIVES.BOOL); + const supportsString = serializer.canSerialize(PRIMITIVES.STRING); + const supportsNull = serializer.canSerialize(PRIMITIVES.NULL); + const supportsUndefined = serializer.canSerialize(PRIMITIVES.UNDEFINED); expect(supportsNumber).toBeTruthy(); expect(supportsBool).toBeTruthy(); @@ -55,7 +53,7 @@ describe('serializers/PrimitiveSerializer', () => it('should tell it cannot deserialize others', () => { - const supportsObject = serializer.canSerialize(objectValue); + const supportsObject = serializer.canSerialize(PRIMITIVES.OBJECT); expect(supportsObject).toBeFalsy(); }); @@ -65,17 +63,17 @@ describe('serializers/PrimitiveSerializer', () => { it('should serialize a primitive', async () => { - const resultNumber = await serializer.serialize(numberValue); - const resultBool = await serializer.serialize(boolValue); - const resultString = await serializer.serialize(stringValue); - const resultNull = await serializer.serialize(nullValue); - const resultUndefined = await serializer.serialize(undefinedValue); - - expect(resultNumber).toEqual(numberValue); - expect(resultBool).toEqual(boolValue); - expect(resultString).toEqual(stringValue); - expect(resultNull).toEqual(nullValue); - expect(resultUndefined).toEqual(undefinedValue); + const resultNumber = await serializer.serialize(PRIMITIVES.NUMBER); + const resultBool = await serializer.serialize(PRIMITIVES.BOOL); + const resultString = await serializer.serialize(PRIMITIVES.STRING); + const resultNull = await serializer.serialize(PRIMITIVES.NULL); + const resultUndefined = await serializer.serialize(PRIMITIVES.UNDEFINED); + + expect(resultNumber).toEqual(PRIMITIVES.NUMBER); + expect(resultBool).toEqual(PRIMITIVES.BOOL); + expect(resultString).toEqual(PRIMITIVES.STRING); + expect(resultNull).toEqual(PRIMITIVES.NULL); + expect(resultUndefined).toEqual(PRIMITIVES.UNDEFINED); }); }); @@ -83,17 +81,17 @@ describe('serializers/PrimitiveSerializer', () => { it('should deserialize a primitive', async () => { - const resultNumber = await serializer.deserialize(numberValue); - const resultBool = await serializer.deserialize(boolValue); - const resultString = await serializer.deserialize(stringValue); - const resultNull = await serializer.deserialize(nullValue); - const resultUndefined = await serializer.deserialize(undefinedValue); - - expect(resultNumber).toEqual(numberValue); - expect(resultBool).toEqual(boolValue); - expect(resultString).toEqual(stringValue); - expect(resultNull).toEqual(nullValue); - expect(resultUndefined).toEqual(undefinedValue); + const resultNumber = await serializer.deserialize(PRIMITIVES.NUMBER); + const resultBool = await serializer.deserialize(PRIMITIVES.BOOL); + const resultString = await serializer.deserialize(PRIMITIVES.STRING); + const resultNull = await serializer.deserialize(PRIMITIVES.NULL); + const resultUndefined = await serializer.deserialize(PRIMITIVES.UNDEFINED); + + expect(resultNumber).toEqual(PRIMITIVES.NUMBER); + expect(resultBool).toEqual(PRIMITIVES.BOOL); + expect(resultString).toEqual(PRIMITIVES.STRING); + expect(resultNull).toEqual(PRIMITIVES.NULL); + expect(resultUndefined).toEqual(PRIMITIVES.UNDEFINED); }); }); }); diff --git a/packages/serialization/test/serializers/RegExpSerializer.spec.ts b/packages/serialization/test/serializers/RegExpSerializer.spec.ts index f2fd6e5e..4c70ff37 100644 --- a/packages/serialization/test/serializers/RegExpSerializer.spec.ts +++ b/packages/serialization/test/serializers/RegExpSerializer.spec.ts @@ -4,13 +4,7 @@ import { describe, expect, it } from 'vitest'; import RegExpSerializer from '../../src/serializers/RegExpSerializer'; import InvalidRegExp from '../../src/serializers/errors/InvalidRegExp'; -import -{ - validRegExp, - serializedValidRegExp, - nonObject, nonRegExp, notSerialized, invalidName, invalidRegExpSource, invalidRegExpFlag, - serializedInvalidRegExpSource, serializedInvalidRegExpFlag -} from '../_fixtures/serializers/RegExpSerializer.fixture'; +import { REGULAR_EXPRESSIONS } from './fixtures'; const serializer = new RegExpSerializer(); @@ -20,15 +14,15 @@ describe('serializers/RegExpSerializer', () => { it('should tell it can serialize an regular expression', () => { - const supportsRegExp = serializer.canSerialize(validRegExp); + const supportsRegExp = serializer.canSerialize(REGULAR_EXPRESSIONS.VALID); expect(supportsRegExp).toBeTruthy(); }); it('should tell it cannot serialize others', () => { - const supportsNonObject = serializer.canSerialize(nonObject); - const supportsNonRegExp = serializer.canSerialize(nonRegExp); + const supportsNonObject = serializer.canSerialize(REGULAR_EXPRESSIONS.NON_OBJECT); + const supportsNonRegExp = serializer.canSerialize(REGULAR_EXPRESSIONS.NON_REGEXP); expect(supportsNonObject).toBeFalsy(); expect(supportsNonRegExp).toBeFalsy(); @@ -39,18 +33,18 @@ describe('serializers/RegExpSerializer', () => { it('should tell it can deserialize an regular expression', () => { - const supportsRegExp = serializer.canDeserialize(serializedValidRegExp); + const supportsRegExp = serializer.canDeserialize(REGULAR_EXPRESSIONS.VALID_SERIALIZED); expect(supportsRegExp).toBeTruthy(); }); it('should tell it cannot deserialize others', () => { - const supportsNonObject = serializer.canDeserialize(nonObject); - const supportsNotSerialized = serializer.canDeserialize(notSerialized); - const supportsInvalidName = serializer.canDeserialize(invalidName); - const supportsInvalidSourceString = serializer.canDeserialize(invalidRegExpSource); - const supportsInvalidFlagString = serializer.canDeserialize(invalidRegExpFlag); + const supportsNonObject = serializer.canDeserialize(REGULAR_EXPRESSIONS.NON_OBJECT); + const supportsNotSerialized = serializer.canDeserialize(REGULAR_EXPRESSIONS.NOT_SERIALIZED); + const supportsInvalidName = serializer.canDeserialize(REGULAR_EXPRESSIONS.INVALID_NAME); + const supportsInvalidSourceString = serializer.canDeserialize(REGULAR_EXPRESSIONS.INVALID_SOURCE); + const supportsInvalidFlagString = serializer.canDeserialize(REGULAR_EXPRESSIONS.INVALID_FLAG); expect(supportsNonObject).toBeFalsy(); expect(supportsNotSerialized).toBeFalsy(); @@ -64,9 +58,9 @@ describe('serializers/RegExpSerializer', () => { it('should serialize a regular expression', async () => { - const resultValidRegExp = await serializer.serialize(validRegExp); + const resultValidRegExp = await serializer.serialize(REGULAR_EXPRESSIONS.VALID); - expect(resultValidRegExp).toStrictEqual(serializedValidRegExp); + expect(resultValidRegExp).toStrictEqual(REGULAR_EXPRESSIONS.VALID_SERIALIZED); }); }); @@ -74,21 +68,21 @@ describe('serializers/RegExpSerializer', () => { it('should deserialize a regular expression', async () => { - const resultValidRegExp = await serializer.deserialize(serializedValidRegExp); + const resultValidRegExp = await serializer.deserialize(REGULAR_EXPRESSIONS.VALID_SERIALIZED); - expect(resultValidRegExp).toStrictEqual(validRegExp); + expect(resultValidRegExp).toStrictEqual(REGULAR_EXPRESSIONS.VALID); }); it('should not deserialize a regular expression with invalid source', async () => { - const deserialize = async () => serializer.deserialize(serializedInvalidRegExpSource); + const deserialize = async () => serializer.deserialize(REGULAR_EXPRESSIONS.INVALID_SOURCE_SERIALIZED); expect(deserialize).rejects.toStrictEqual(new InvalidRegExp('sel/\\', 'g')); }); it('should not deserialize a regular expression with invalid flag', async () => { - const deserialize = async () => serializer.deserialize(serializedInvalidRegExpFlag); + const deserialize = async () => serializer.deserialize(REGULAR_EXPRESSIONS.INVALID_FLAG_SERIALIZED); expect(deserialize).rejects.toStrictEqual(new InvalidRegExp('w+', true)); }); diff --git a/packages/serialization/test/serializers/SetSerializer.spec.ts b/packages/serialization/test/serializers/SetSerializer.spec.ts index 80c1aa27..c70f4eaa 100644 --- a/packages/serialization/test/serializers/SetSerializer.spec.ts +++ b/packages/serialization/test/serializers/SetSerializer.spec.ts @@ -1,17 +1,17 @@ import { describe, expect, it } from 'vitest'; +import Serializer from '../../src/Serializer'; +import PrimitiveSerializer from '../../src/serializers/PrimitiveSerializer'; import SetSerializer from '../../src/serializers/SetSerializer'; -import { - parent, - emptySet, mixedSet, nestedSet, - serializedEmptySet, serializedMixedSet, serializedNestedSet, - nonObject, nonSet, notSerialized, invalidName, invalidValues -} from '../_fixtures/serializers/SetSerializer.fixture'; +import { SETS } from './fixtures'; const serializer = new SetSerializer(); -serializer.parent = parent; +const parent = new Serializer(); + +parent.addSerializer(serializer); +parent.addSerializer(new PrimitiveSerializer()); describe('serializers/SetSerializer', () => { @@ -19,15 +19,15 @@ describe('serializers/SetSerializer', () => { it('should tell it can serialize a set', () => { - const supportsSet = serializer.canSerialize(emptySet); + const supportsSet = serializer.canSerialize(SETS.EMPTY); expect(supportsSet).toBeTruthy(); }); it('should tell it cannot serialize others', () => { - const supportsNonObject = serializer.canSerialize(nonObject); - const supportsNonSet = serializer.canSerialize(nonSet); + const supportsNonObject = serializer.canSerialize(SETS.NON_OBJECT); + const supportsNonSet = serializer.canSerialize(SETS.NON_SET); expect(supportsNonObject).toBeFalsy(); expect(supportsNonSet).toBeFalsy(); @@ -38,17 +38,17 @@ describe('serializers/SetSerializer', () => { it('should tell it can deserialize a set', () => { - const supportsSet = serializer.canDeserialize(serializedEmptySet); + const supportsSet = serializer.canDeserialize(SETS.EMPTY_SERIALIZED); expect(supportsSet).toBeTruthy(); }); it('should tell it cannot deserialize others', () => { - const supportsNonObject = serializer.canDeserialize(nonObject); - const supportsNotSerialized = serializer.canDeserialize(notSerialized); - const supportsInvalidName = serializer.canDeserialize(invalidName); - const supportsInvalidValues = serializer.canDeserialize(invalidValues); + const supportsNonObject = serializer.canDeserialize(SETS.NON_OBJECT); + const supportsNotSerialized = serializer.canDeserialize(SETS.NOT_SERIALIZED); + const supportsInvalidName = serializer.canDeserialize(SETS.INVALID_NAME); + const supportsInvalidValues = serializer.canDeserialize(SETS.INVALID_VALUES); expect(supportsNonObject).toBeFalsy(); expect(supportsNotSerialized).toBeFalsy(); @@ -61,13 +61,13 @@ describe('serializers/SetSerializer', () => { it('should serialize a set', async () => { - const resultEmptySet = await serializer.serialize(emptySet); - const resultMixedSet = await serializer.serialize(mixedSet); - const resultNestedSet = await serializer.serialize(nestedSet); + const resultEmptySet = await serializer.serialize(SETS.EMPTY); + const resultMixedSet = await serializer.serialize(SETS.MIXED); + const resultNestedSet = await serializer.serialize(SETS.NESTED); - expect(resultEmptySet).toStrictEqual(serializedEmptySet); - expect(resultMixedSet).toStrictEqual(serializedMixedSet); - expect(resultNestedSet).toStrictEqual(serializedNestedSet); + expect(resultEmptySet).toStrictEqual(SETS.EMPTY_SERIALIZED); + expect(resultMixedSet).toStrictEqual(SETS.MIXED_SERIALIZED); + expect(resultNestedSet).toStrictEqual(SETS.NESTED_SERIALIZED); }); }); @@ -75,13 +75,13 @@ describe('serializers/SetSerializer', () => { it('should deserialize a set', async () => { - const resultEmptySet = await serializer.deserialize(serializedEmptySet); - const resultMixedSet = await serializer.deserialize(serializedMixedSet); - const resultNestedSet = await serializer.deserialize(serializedNestedSet); + const resultEmptySet = await serializer.deserialize(SETS.EMPTY_SERIALIZED); + const resultMixedSet = await serializer.deserialize(SETS.MIXED_SERIALIZED); + const resultNestedSet = await serializer.deserialize(SETS.NESTED_SERIALIZED); - expect(resultEmptySet).toStrictEqual(emptySet); - expect(resultMixedSet).toStrictEqual(mixedSet); - expect(resultNestedSet).toStrictEqual(nestedSet); + expect(resultEmptySet).toStrictEqual(SETS.EMPTY); + expect(resultMixedSet).toStrictEqual(SETS.MIXED); + expect(resultNestedSet).toStrictEqual(SETS.NESTED); }); }); }); diff --git a/packages/serialization/test/serializers/TypedArraySerializer.spec.ts b/packages/serialization/test/serializers/TypedArraySerializer.spec.ts index 70fd1156..39d62309 100644 --- a/packages/serialization/test/serializers/TypedArraySerializer.spec.ts +++ b/packages/serialization/test/serializers/TypedArraySerializer.spec.ts @@ -3,11 +3,7 @@ import { describe, expect, it } from 'vitest'; import TypedArraySerializer from '../../src/serializers/TypedArraySerializer'; -import { - viewUint16, viewInt8, viewBigInt64, - serializedViewUint16, serializedViewInt8, serializedViewBigInt64, - nonObject, plainObject, notSerialized, invalidName, invalidType, invalidBytes -} from '../_fixtures/serializers/TypedArraySerializer.fixture'; +import { TYPED_ARRAYS } from './fixtures'; const serializer = new TypedArraySerializer(); @@ -17,9 +13,9 @@ describe('serializers/TypedArraySerializer', () => { it('should tell it can serialize an array buffer', () => { - const supportsUint16 = serializer.canSerialize(viewUint16); - const supportsInt8 = serializer.canSerialize(viewInt8); - const supportsBigInt64 = serializer.canSerialize(viewBigInt64); + const supportsUint16 = serializer.canSerialize(TYPED_ARRAYS.UINT16); + const supportsInt8 = serializer.canSerialize(TYPED_ARRAYS.INT8); + const supportsBigInt64 = serializer.canSerialize(TYPED_ARRAYS.BIG_INT64); expect(supportsUint16).toBeTruthy(); expect(supportsInt8).toBeTruthy(); @@ -28,8 +24,8 @@ describe('serializers/TypedArraySerializer', () => it('should tell it cannot serialize others', () => { - const supportsPlainObject = serializer.canSerialize(plainObject); - const supportsNonObject = serializer.canSerialize(nonObject); + const supportsPlainObject = serializer.canSerialize(TYPED_ARRAYS.PLAIN_OBJECT); + const supportsNonObject = serializer.canSerialize(TYPED_ARRAYS.NON_OBJECT); expect(supportsPlainObject).toBeFalsy(); expect(supportsNonObject).toBeFalsy(); @@ -40,9 +36,9 @@ describe('serializers/TypedArraySerializer', () => { it('should tell it can deserialize an array buffer', () => { - const supportsUint16 = serializer.canDeserialize(serializedViewUint16); - const supportsInt8 = serializer.canDeserialize(serializedViewInt8); - const supportsBigInt64 = serializer.canDeserialize(serializedViewBigInt64); + const supportsUint16 = serializer.canDeserialize(TYPED_ARRAYS.UINT16_SERIALIZED); + const supportsInt8 = serializer.canDeserialize(TYPED_ARRAYS.INT8_SERIALIZED); + const supportsBigInt64 = serializer.canDeserialize(TYPED_ARRAYS.BIG_INT64_SERIALIZED); expect(supportsUint16).toBeTruthy(); expect(supportsInt8).toBeTruthy(); @@ -51,11 +47,11 @@ describe('serializers/TypedArraySerializer', () => it('should tell it cannot deserialize others', () => { - const supportsNonObject = serializer.canDeserialize(nonObject); - const supportsNotSerialized = serializer.canDeserialize(notSerialized); - const supportsInvalidName = serializer.canDeserialize(invalidName); - const supportsInvalidType = serializer.canDeserialize(invalidType); - const supportsInvalidBytes = serializer.canDeserialize(invalidBytes); + const supportsNonObject = serializer.canDeserialize(TYPED_ARRAYS.NON_OBJECT); + const supportsNotSerialized = serializer.canDeserialize(TYPED_ARRAYS.NOT_SERIALIZED); + const supportsInvalidName = serializer.canDeserialize(TYPED_ARRAYS.INVALID_NAME); + const supportsInvalidType = serializer.canDeserialize(TYPED_ARRAYS.INVALID_TYPE); + const supportsInvalidBytes = serializer.canDeserialize(TYPED_ARRAYS.INVALID_BYTES); expect(supportsNonObject).toBeFalsy(); expect(supportsNotSerialized).toBeFalsy(); @@ -69,13 +65,13 @@ describe('serializers/TypedArraySerializer', () => { it('should serialize an array buffer', async () => { - const resultUint16 = await serializer.serialize(viewUint16); - const resultInt8 = await serializer.serialize(viewInt8); - const resultBigInt64 = await serializer.serialize(viewBigInt64); + const resultUint16 = await serializer.serialize(TYPED_ARRAYS.UINT16); + const resultInt8 = await serializer.serialize(TYPED_ARRAYS.INT8); + const resultBigInt64 = await serializer.serialize(TYPED_ARRAYS.BIG_INT64); - expect(resultUint16).toStrictEqual(serializedViewUint16); - expect(resultInt8).toStrictEqual(serializedViewInt8); - expect(resultBigInt64).toStrictEqual(serializedViewBigInt64); + expect(resultUint16).toStrictEqual(TYPED_ARRAYS.UINT16_SERIALIZED); + expect(resultInt8).toStrictEqual(TYPED_ARRAYS.INT8_SERIALIZED); + expect(resultBigInt64).toStrictEqual(TYPED_ARRAYS.BIG_INT64_SERIALIZED); }); }); @@ -83,13 +79,13 @@ describe('serializers/TypedArraySerializer', () => { it('should deserialize an array buffer', async () => { - const resultUint16 = await serializer.deserialize(serializedViewUint16); - const resultInt8 = await serializer.deserialize(serializedViewInt8); - const resultBigInt64 = await serializer.deserialize(serializedViewBigInt64); + const resultUint16 = await serializer.deserialize(TYPED_ARRAYS.UINT16_SERIALIZED); + const resultInt8 = await serializer.deserialize(TYPED_ARRAYS.INT8_SERIALIZED); + const resultBigInt64 = await serializer.deserialize(TYPED_ARRAYS.BIG_INT64_SERIALIZED); - expect(resultUint16).toStrictEqual(viewUint16); - expect(resultInt8).toStrictEqual(viewInt8); - expect(resultBigInt64).toStrictEqual(viewBigInt64); + expect(resultUint16).toStrictEqual(TYPED_ARRAYS.UINT16); + expect(resultInt8).toStrictEqual(TYPED_ARRAYS.INT8); + expect(resultBigInt64).toStrictEqual(TYPED_ARRAYS.BIG_INT64); }); }); }); diff --git a/packages/serialization/test/serializers/UrlSerializer.spec.ts b/packages/serialization/test/serializers/UrlSerializer.spec.ts index e4b0e614..a399ae89 100644 --- a/packages/serialization/test/serializers/UrlSerializer.spec.ts +++ b/packages/serialization/test/serializers/UrlSerializer.spec.ts @@ -4,12 +4,7 @@ import { describe, expect, it } from 'vitest'; import UrlSerializer from '../../src/serializers/UrlSerializer'; import InvalidUrlString from '../../src/serializers/errors/InvalidUrlString'; -import -{ - validUrl, - serializedValidUrl, - nonObject, nonUrl, notSerialized, invalidName, invalidUrlValue, invalidUrlString -} from '../_fixtures/serializers/UrlSerializer.fixture'; +import { URLS } from './fixtures'; const serializer = new UrlSerializer(); @@ -19,15 +14,15 @@ describe('serializers/UrlSerializer', () => { it('should tell it can serialize an url', () => { - const supportsUrl = serializer.canSerialize(validUrl); + const supportsUrl = serializer.canSerialize(URLS.VALID); expect(supportsUrl).toBeTruthy(); }); it('should tell it cannot serialize others', () => { - const supportsNonObject = serializer.canSerialize(nonObject); - const supportsNonUrl = serializer.canSerialize(nonUrl); + const supportsNonObject = serializer.canSerialize(URLS.NON_OBJECT); + const supportsNonUrl = serializer.canSerialize(URLS.NON_URL); expect(supportsNonObject).toBeFalsy(); expect(supportsNonUrl).toBeFalsy(); @@ -38,17 +33,17 @@ describe('serializers/UrlSerializer', () => { it('should tell it can deserialize an url', () => { - const supportsUrl = serializer.canDeserialize(serializedValidUrl); + const supportsUrl = serializer.canDeserialize(URLS.VALID_SERIALIZED); expect(supportsUrl).toBeTruthy(); }); it('should tell it cannot deserialize others', () => { - const supportsNonObject = serializer.canDeserialize(nonObject); - const supportsNotSerialized = serializer.canDeserialize(notSerialized); - const supportsInvalidName = serializer.canDeserialize(invalidName); - const supportsInvalidUrlValue = serializer.canDeserialize(invalidUrlValue); + const supportsNonObject = serializer.canDeserialize(URLS.NON_OBJECT); + const supportsNotSerialized = serializer.canDeserialize(URLS.NOT_SERIALIZED); + const supportsInvalidName = serializer.canDeserialize(URLS.INVALID_NAME); + const supportsInvalidUrlValue = serializer.canDeserialize(URLS.INVALID_URL_VALUE); expect(supportsNonObject).toBeFalsy(); expect(supportsNotSerialized).toBeFalsy(); @@ -61,9 +56,9 @@ describe('serializers/UrlSerializer', () => { it('should serialize a url', async () => { - const resultValidUrl = await serializer.serialize(validUrl); + const resultValidUrl = await serializer.serialize(URLS.VALID); - expect(resultValidUrl).toStrictEqual(serializedValidUrl); + expect(resultValidUrl).toStrictEqual(URLS.VALID_SERIALIZED); }); }); @@ -71,14 +66,14 @@ describe('serializers/UrlSerializer', () => { it('should deserialize an url', async () => { - const resultValidUrl = await serializer.deserialize(serializedValidUrl); + const resultValidUrl = await serializer.deserialize(URLS.VALID_SERIALIZED); - expect(resultValidUrl).toStrictEqual(validUrl); + expect(resultValidUrl).toStrictEqual(URLS.VALID); }); it('should not deserialize an url with an invalid url string', async () => { - const deserialize = async () => serializer.deserialize(invalidUrlString); + const deserialize = async () => serializer.deserialize(URLS.INVALID_URL_STRING); expect(deserialize).rejects.toStrictEqual(new InvalidUrlString('example')); }); diff --git a/packages/serialization/test/serializers/fixtures/arrays.fixture.ts b/packages/serialization/test/serializers/fixtures/arrays.fixture.ts new file mode 100644 index 00000000..ba297d0e --- /dev/null +++ b/packages/serialization/test/serializers/fixtures/arrays.fixture.ts @@ -0,0 +1,16 @@ + +const emptyArray: unknown[] = []; +const mixedArray: unknown[] = ['a', 1, true]; +const nestedArray: unknown[] = ['b', 2, ['c', false], true]; + +const nonObject = 42; +const nonArray = new Map(); + +export const ARRAYS = +{ + EMPTY: emptyArray, + MIXED: mixedArray, + NESTED: nestedArray, + NON_OBJECT: nonObject, + NON_ARRAY: nonArray, +}; diff --git a/packages/serialization/test/_fixtures/serializers/BigIntSerializer.fixture.ts b/packages/serialization/test/serializers/fixtures/bigIntegers.fixture.ts similarity index 65% rename from packages/serialization/test/_fixtures/serializers/BigIntSerializer.fixture.ts rename to packages/serialization/test/serializers/fixtures/bigIntegers.fixture.ts index fb2b737d..0786d835 100644 --- a/packages/serialization/test/_fixtures/serializers/BigIntSerializer.fixture.ts +++ b/packages/serialization/test/serializers/fixtures/bigIntegers.fixture.ts @@ -10,9 +10,14 @@ const invalidName = { serialized: true, name: 'Map', value: '7124811545485115646 const invalidBigIntValue = { serialized: true, name: 'BigInt', value: true }; const invalidBigIntString = { serialized: true, name: 'BigInt', value: '1.3' }; -export +export const BIG_INTEGERS = { - validBigInt, - serializedValidBigInt, - nonObject, nonBigInt, notSerialized, invalidName, invalidBigIntValue, invalidBigIntString + VALID: validBigInt, + VALID_SERIALIZED: serializedValidBigInt, + NON_OBJECT: nonObject, + NON_BIGINT: nonBigInt, + NOT_SERIALIZED: notSerialized, + INVALID_NAME: invalidName, + INVALID_BIGINT_VALUE: invalidBigIntValue, + INVALID_BIGINT_STRING: invalidBigIntString }; diff --git a/packages/serialization/test/serializers/fixtures/classResolver.fixture.ts b/packages/serialization/test/serializers/fixtures/classResolver.fixture.ts new file mode 100644 index 00000000..96fe0fc5 --- /dev/null +++ b/packages/serialization/test/serializers/fixtures/classResolver.fixture.ts @@ -0,0 +1,27 @@ + +import ClassResolver from '../../../src/interfaces/ClassResolver'; + +import { CLASSES } from './classes.fixture'; + +class MockClassResolver implements ClassResolver +{ + resolveKey(clazz: Function): string | undefined + { + return clazz.name; + } + + resolveClass(key: string): Function | undefined + { + switch (key) + { + case 'Data': return CLASSES.Data; + case 'Constructed': return CLASSES.Constructed; + case 'Nested': return CLASSES.Nested; + case 'PrivateFieldClass': return CLASSES.PrivateFieldClass; + } + + return undefined; + } +} + +export const classResolver = new MockClassResolver(); diff --git a/packages/serialization/test/serializers/fixtures/classes.fixture.ts b/packages/serialization/test/serializers/fixtures/classes.fixture.ts new file mode 100644 index 00000000..6dce9069 --- /dev/null +++ b/packages/serialization/test/serializers/fixtures/classes.fixture.ts @@ -0,0 +1,103 @@ + +class Data +{ + a?: number; + b?: boolean; +} + +class Constructed +{ + #a: number; + #b: boolean; + #c?: string; + + constructor(a: number, b: boolean) + { + this.#a = a; + this.#b = b; + } + + get a() { return this.#a; } + + get b() { return this.#b; } + + get c() { return this.#c; } + + set c(c) { this.#c = c; } +} + +class Nested +{ + name: string; + #constructed: Constructed; + + constructor(name: string, constructed: Constructed) + { + this.name = name; + this.#constructed = constructed; + } + + get constructed() { return this.#constructed; } +} + +class PrivateFieldClass +{ + #a: number; + #b: boolean; + #c?: string; + + constructor(a: number, b: boolean, c?: string) + { + this.#a = a; + this.#b = b; + this.#c = c; + } + + get d() { return this.#a; } +} + +const dataClass = new Data(); +dataClass.a = 1; +dataClass.b = true; + +const constructedClass = new Constructed(1, true); +constructedClass.c = 'hello'; + +const nestedClass = new Nested('hello', constructedClass); +const privateClass = new PrivateFieldClass(1, true); + +const serializedDataClass = { serialized: true, name: 'class', key: 'Data', args: {}, fields: { a: 1, b: true } }; +const serializedConstructedClass = { serialized: true, name: 'class', key: 'Constructed', args: { '0': 1, '1': true }, fields: { c: 'hello' } }; +const serializedNestedClass = { serialized: true, name: 'class', key: 'Nested', args: { '0': 'hello', '1': { serialized: true, name: 'class', key: 'Constructed', args: { '0': 1, '1': true }, fields: { c: 'hello' } } }, fields: {} }; +const serializedPrivateClass = { serialized: true, name: 'class', key: 'PrivateFieldClass', args: { '0': undefined, '1': undefined, '2': undefined }, fields: { } }; + +const serializedInvalidClass = { serialized: true, name: 'class', key: 'Invalid', args: {}, fields: {} }; +const serializedUnserializableClass = { serialized: true, name: 'class', key: 'Infinity', args: {}, fields: {} }; + +const nonObject = 42; +const nonClassObject = new Object(); +const notSerialized = { name: 'class', key: 'Unserialized', args: {}, fields: {} }; +const invalidKey = { serialized: true, name: 'class', key: 123, args: {}, fields: {} }; +const invalidArgs = { serialized: true, name: 'class', key: 'Invalid', args: [], fields: {} }; +const invalidFields = { serialized: true, name: 'class', key: 'Invalid', args: {}, fields: [] }; + +export const CLASSES = +{ + Data, Constructed, Nested, PrivateFieldClass, + DATA_INSTANCE: dataClass, + DATA_SERIALIZED: serializedDataClass, + CONSTRUCTED_INSTANCE: constructedClass, + CONSTRUCTED_SERIALIZED: serializedConstructedClass, + NESTED_INSTANCE: nestedClass, + NESTED_SERIALIZED: serializedNestedClass, + PRIVATE_INSTANCE: privateClass, + PRIVATE_SERIALIZED: serializedPrivateClass, + INVALID_SERIALIZED: serializedInvalidClass, + UNSERIALIZABLE: serializedUnserializableClass, + NON_OBJECT: nonObject, + NON_CLASS_OBJECT: nonClassObject, + NOT_SERIALIZED: notSerialized, + INVALID_KEY: invalidKey, + INVALID_ARGS: invalidArgs, + INVALID_FIELDS: invalidFields +}; diff --git a/packages/serialization/test/_fixtures/serializers/DateSerializer.fixture.ts b/packages/serialization/test/serializers/fixtures/dates.fixture.ts similarity index 64% rename from packages/serialization/test/_fixtures/serializers/DateSerializer.fixture.ts rename to packages/serialization/test/serializers/fixtures/dates.fixture.ts index ac0ab67e..a42c37a0 100644 --- a/packages/serialization/test/_fixtures/serializers/DateSerializer.fixture.ts +++ b/packages/serialization/test/serializers/fixtures/dates.fixture.ts @@ -12,8 +12,14 @@ const invalidName = { serialized: true, name: 'Map', value: DATE_TIME_STRING }; const invalidDateValue = { serialized: true, name: 'Date', value: true }; const invalidDateString = { serialized: true, name: 'Date', value: 'hello' }; -export { - fixedDate, - serializedFixedDate, - nonObject, nonDate, notSerialized, invalidName, invalidDateValue, invalidDateString +export const DATES = +{ + FIXED: fixedDate, + FIXED_SERIALIZED: serializedFixedDate, + NON_OBJECT: nonObject, + NON_DATE: nonDate, + NOT_SERIALIZED: notSerialized, + INVALID_NAME: invalidName, + INVALID_DATE_VALUE: invalidDateValue, + INVALID_DATE_STRING: invalidDateString }; diff --git a/packages/serialization/test/_fixtures/serializers/ErrorSerializer.fixture.ts b/packages/serialization/test/serializers/fixtures/errors.fixture.ts similarity index 75% rename from packages/serialization/test/_fixtures/serializers/ErrorSerializer.fixture.ts rename to packages/serialization/test/serializers/fixtures/errors.fixture.ts index 641f3794..315a96f6 100644 --- a/packages/serialization/test/_fixtures/serializers/ErrorSerializer.fixture.ts +++ b/packages/serialization/test/serializers/fixtures/errors.fixture.ts @@ -32,9 +32,25 @@ const notSerialized = { name: 'Error', type: 'Error', stack: undefined, message: const invalidName = { serialized: true, name: 'CustomError', type: 'Error', stack: undefined, message: 'error', cause: undefined }; const invalidType = { serialized: true, name: 'Error', type: 'CustomError', stack: undefined, message: 'error', cause: undefined }; -export { - plainError, evalError, rangeError, referenceError, syntaxError, typeError, uriError, - serializedPlainError, serializedEvalError, serializedRangeError, serializedReferenceError, serializedSyntaxError, serializedTypeError, serializedURIError, - customError, otherClass, - notSerialized, invalidName, invalidType +export const ERRORS = +{ + PLAIN: plainError, + PLAIN_SERIALIZED: serializedPlainError, + EVAL: evalError, + EVAL_SERIALIZED: serializedEvalError, + RANGE: rangeError, + RANGE_SERIALIZED: serializedRangeError, + REFERENCE: referenceError, + REFERENCE_SERIALIZED: serializedReferenceError, + SYNTAX: syntaxError, + SYNTAX_SERIALIZED: serializedSyntaxError, + TYPE: typeError, + TYPE_SERIALIZED: serializedTypeError, + URI: uriError, + URI_SERIALIZED: serializedURIError, + CUSTOM: customError, + OTHER_CLASS: otherClass, + NOT_SERIALIZED: notSerialized, + INVALID_NAME: invalidName, + INVALID_TYPE: invalidType }; diff --git a/packages/serialization/test/serializers/fixtures/index.ts b/packages/serialization/test/serializers/fixtures/index.ts new file mode 100644 index 00000000..31f53065 --- /dev/null +++ b/packages/serialization/test/serializers/fixtures/index.ts @@ -0,0 +1,14 @@ + +export * from './arrays.fixture'; +export * from './bigIntegers.fixture'; +export * from './classes.fixture'; +export * from './classResolver.fixture'; +export * from './dates.fixture'; +export * from './errors.fixture'; +export * from './maps.fixture'; +export * from './objects.fixture'; +export * from './primitives.fixture'; +export * from './regularExpressions.fixture'; +export * from './sets.fixture'; +export * from './typedArrays.fixture'; +export * from './urls.fixture'; diff --git a/packages/serialization/test/_fixtures/serializers/MapSerializer.fixture.ts b/packages/serialization/test/serializers/fixtures/maps.fixture.ts similarity index 65% rename from packages/serialization/test/_fixtures/serializers/MapSerializer.fixture.ts rename to packages/serialization/test/serializers/fixtures/maps.fixture.ts index 5e603f6d..f2342a3d 100644 --- a/packages/serialization/test/_fixtures/serializers/MapSerializer.fixture.ts +++ b/packages/serialization/test/serializers/fixtures/maps.fixture.ts @@ -1,12 +1,4 @@ -import Serializer from '../../../src/Serializer'; -import PrimitiveSerializer from '../../../src/serializers/PrimitiveSerializer'; -import MapSerializer from '../../../src/serializers/MapSerializer'; - -const parent = new Serializer(); -parent.addSerializer(new MapSerializer()); -parent.addSerializer(new PrimitiveSerializer()); - const emptyMap: Map = new Map(); const mixedMap: Map = new Map().set('a', 1).set('b', true); const nestedMap: Map = new Map().set('b', 'hello').set('c', new Map().set('d', false)); @@ -22,9 +14,18 @@ const invalidName = { serialized: true, name: 'Set', entries: [], values: [] }; const invalidKeys = { serialized: true, name: 'Map', entries: {}, values: [] }; const invalidValues = { serialized: true, name: 'Map', entries: [], values: {} }; -export { - parent, - emptyMap, mixedMap, nestedMap, - serializedEmptyMap, serializedMixedMap, serializedNestedMap, - nonObject, nonMap, notSerialized, invalidName, invalidKeys, invalidValues +export const MAPS = +{ + EMPTY: emptyMap, + EMPTY_SERIALIZED: serializedEmptyMap, + MIXED: mixedMap, + MIXED_SERIALIZED: serializedMixedMap, + NESTED: nestedMap, + NESTED_SERIALIZED: serializedNestedMap, + NON_OBJECT: nonObject, + NON_MAP: nonMap, + NOT_SERIALIZED: notSerialized, + INVALID_NAME: invalidName, + INVALID_KEYS: invalidKeys, + INVALID_VALUES: invalidValues }; diff --git a/packages/serialization/test/serializers/fixtures/objects.fixture.ts b/packages/serialization/test/serializers/fixtures/objects.fixture.ts new file mode 100644 index 00000000..3e41ea41 --- /dev/null +++ b/packages/serialization/test/serializers/fixtures/objects.fixture.ts @@ -0,0 +1,16 @@ + +const emptyObject = {}; +const mixedObject = { a: 1, b: true, c: 'hello' }; +const nestedObject = { a: 1, b: true, c: { d: false, e: true } }; + +const nonObject = 42; +const specificObject = new Map(); + +export const OBJECTS = +{ + EMPTY: emptyObject, + MIXED: mixedObject, + NESTED: nestedObject, + NON_OBJECT: nonObject, + SPECIFIC_OBJECT: specificObject +}; diff --git a/packages/serialization/test/serializers/fixtures/primitives.fixture.ts b/packages/serialization/test/serializers/fixtures/primitives.fixture.ts new file mode 100644 index 00000000..103425c0 --- /dev/null +++ b/packages/serialization/test/serializers/fixtures/primitives.fixture.ts @@ -0,0 +1,17 @@ + +const numberValue = 1; +const boolValue = true; +const stringValue = 'hello'; +const nullValue = null; +const undefinedValue = undefined; +const objectValue = {}; + +export const PRIMITIVES = +{ + NUMBER: numberValue, + BOOL: boolValue, + STRING: stringValue, + NULL: nullValue, + UNDEFINED: undefinedValue, + OBJECT: objectValue +}; diff --git a/packages/serialization/test/_fixtures/serializers/RegExpSerializer.fixture.ts b/packages/serialization/test/serializers/fixtures/regularExpressions.fixture.ts similarity index 63% rename from packages/serialization/test/_fixtures/serializers/RegExpSerializer.fixture.ts rename to packages/serialization/test/serializers/fixtures/regularExpressions.fixture.ts index b7101663..1db86ab1 100644 --- a/packages/serialization/test/_fixtures/serializers/RegExpSerializer.fixture.ts +++ b/packages/serialization/test/serializers/fixtures/regularExpressions.fixture.ts @@ -12,10 +12,16 @@ const invalidRegExpFlag = { serialized: true, name: 'RegExp', source: 'w+', flag const serializedInvalidRegExpSource = { serialized: true, name: 'RegExp', source: 'sel/\\', flags: 'g' }; const serializedInvalidRegExpFlag = { serialized: true, name: 'RegExp', source: 'w+', flags: 'true' }; -export +export const REGULAR_EXPRESSIONS = { - validRegExp, - serializedValidRegExp, - nonObject, nonRegExp, notSerialized, invalidName, invalidRegExpSource, invalidRegExpFlag, - serializedInvalidRegExpSource, serializedInvalidRegExpFlag + VALID: validRegExp, + VALID_SERIALIZED: serializedValidRegExp, + NON_OBJECT: nonObject, + NON_REGEXP: nonRegExp, + NOT_SERIALIZED: notSerialized, + INVALID_NAME: invalidName, + INVALID_SOURCE: invalidRegExpSource, + INVALID_FLAG: invalidRegExpFlag, + INVALID_SOURCE_SERIALIZED: serializedInvalidRegExpSource, + INVALID_FLAG_SERIALIZED: serializedInvalidRegExpFlag }; diff --git a/packages/serialization/test/_fixtures/serializers/SetSerializer.fixture.ts b/packages/serialization/test/serializers/fixtures/sets.fixture.ts similarity index 58% rename from packages/serialization/test/_fixtures/serializers/SetSerializer.fixture.ts rename to packages/serialization/test/serializers/fixtures/sets.fixture.ts index 94bc83ad..a49d5000 100644 --- a/packages/serialization/test/_fixtures/serializers/SetSerializer.fixture.ts +++ b/packages/serialization/test/serializers/fixtures/sets.fixture.ts @@ -1,12 +1,4 @@ -import Serializer from '../../../src/Serializer'; -import PrimitiveSerializer from '../../../src/serializers/PrimitiveSerializer'; -import SetSerializer from '../../../src/serializers/SetSerializer'; - -const parent = new Serializer(); -parent.addSerializer(new SetSerializer()); -parent.addSerializer(new PrimitiveSerializer()); - const emptySet: Set = new Set(); const mixedSet: Set = new Set().add(1).add(true); const nestedSet: Set = new Set().add('hello').add(new Set().add(false)); @@ -21,9 +13,17 @@ const notSerialized = { name: 'Set', bytes: [] }; const invalidName = { serialized: true, name: 'Map', values: [] }; const invalidValues = { serialized: true, name: 'Set', values: {} }; -export { - parent, - emptySet, mixedSet, nestedSet, - serializedEmptySet, serializedMixedSet, serializedNestedSet, - nonObject, nonSet, notSerialized, invalidName, invalidValues +export const SETS = +{ + EMPTY: emptySet, + EMPTY_SERIALIZED: serializedEmptySet, + MIXED: mixedSet, + MIXED_SERIALIZED: serializedMixedSet, + NESTED: nestedSet, + NESTED_SERIALIZED: serializedNestedSet, + NON_OBJECT: nonObject, + NON_SET: nonSet, + NOT_SERIALIZED: notSerialized, + INVALID_NAME: invalidName, + INVALID_VALUES: invalidValues }; diff --git a/packages/serialization/test/_fixtures/serializers/TypedArraySerializer.fixture.ts b/packages/serialization/test/serializers/fixtures/typedArrays.fixture.ts similarity index 81% rename from packages/serialization/test/_fixtures/serializers/TypedArraySerializer.fixture.ts rename to packages/serialization/test/serializers/fixtures/typedArrays.fixture.ts index d9f1b521..3e9de339 100644 --- a/packages/serialization/test/_fixtures/serializers/TypedArraySerializer.fixture.ts +++ b/packages/serialization/test/serializers/fixtures/typedArrays.fixture.ts @@ -53,8 +53,18 @@ const invalidName = { serialized: true, name: 'OtherBuffer', type: 'Uint16Array' const invalidType = { serialized: true, name: 'TypedArray', type: 'Int42Array', bytes: [] }; const invalidBytes = { serialized: true, name: 'TypedArray', type: 'Uint16Array', bytes: {} }; -export { - viewUint16, viewInt8, viewBigInt64, - serializedViewUint16, serializedViewInt8, serializedViewBigInt64, - nonObject, plainObject, notSerialized, invalidName, invalidType, invalidBytes +export const TYPED_ARRAYS = +{ + UINT16: viewUint16, + UINT16_SERIALIZED: serializedViewUint16, + INT8: viewInt8, + INT8_SERIALIZED: serializedViewInt8, + BIG_INT64: viewBigInt64, + BIG_INT64_SERIALIZED: serializedViewBigInt64, + NON_OBJECT: nonObject, + PLAIN_OBJECT: plainObject, + NOT_SERIALIZED: notSerialized, + INVALID_NAME: invalidName, + INVALID_TYPE: invalidType, + INVALID_BYTES: invalidBytes }; diff --git a/packages/serialization/test/_fixtures/serializers/UrlSerializer.fixture.ts b/packages/serialization/test/serializers/fixtures/urls.fixture.ts similarity index 65% rename from packages/serialization/test/_fixtures/serializers/UrlSerializer.fixture.ts rename to packages/serialization/test/serializers/fixtures/urls.fixture.ts index f85cd8da..4ead8994 100644 --- a/packages/serialization/test/_fixtures/serializers/UrlSerializer.fixture.ts +++ b/packages/serialization/test/serializers/fixtures/urls.fixture.ts @@ -10,9 +10,14 @@ const invalidName = { serialized: true, name: 'Map', value: '2021-01-01T00:00:00 const invalidUrlValue = { serialized: true, name: 'Url', value: true }; const invalidUrlString = { serialized: true, name: 'Url', value: 'example' }; -export +export const URLS = { - validUrl, - serializedValidUrl, - nonObject, nonUrl, notSerialized, invalidName, invalidUrlValue, invalidUrlString + VALID: validUrl, + VALID_SERIALIZED: serializedValidUrl, + NON_OBJECT: nonObject, + NON_URL: nonUrl, + NOT_SERIALIZED: notSerialized, + INVALID_NAME: invalidName, + INVALID_URL_VALUE: invalidUrlValue, + INVALID_URL_STRING: invalidUrlString }; diff --git a/packages/server-nodejs/README.md b/packages/server-nodejs/README.md deleted file mode 100644 index eff9f4f7..00000000 --- a/packages/server-nodejs/README.md +++ /dev/null @@ -1,9 +0,0 @@ - -# Jitar Server for NodeJS - -This package contains the [NodeJS](https://nodejs.org/) server implementation to run [Jitar](https://jitar.dev) applications. - -For more information about Jitar: - -* [Visit our website](https://jitar.dev) -* [Read the documentation](https://docs.jitar.dev). diff --git a/packages/server-nodejs/package.json b/packages/server-nodejs/package.json deleted file mode 100644 index 868a61ef..00000000 --- a/packages/server-nodejs/package.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "name": "@jitar/server-nodejs", - "version": "0.7.5", - "description": "NodeJS server implementation for Jitar.", - "author": "Masking Technology (https://jitar.dev)", - "license": "MIT", - "type": "module", - "types": "dist/lib.d.ts", - "sideEffects": true, - "exports": { - ".": "./dist/lib.js", - "./server.js": "./dist/server.js" - }, - "files": [ - "CHANGELOG.md", - "README.md", - "dist" - ], - "publishConfig": { - "access": "public" - }, - "scripts": { - "lint": "eslint . --ext .ts", - "build": "tsc -p tsconfig.json", - "clean": "rm -rf dist build", - "prepublishOnly": "npm run clean && npm run build" - }, - "dependencies": { - "@jitar/caching": "*", - "@jitar/runtime": "*", - "express": "^4.19.2", - "express-http-proxy": "^2.0.0", - "fs-extra": "^11.2.0", - "glob": "10.4.3", - "mime-types": "^2.1.35", - "tslog": "^4.9.3", - "yargs": "^17.7.2", - "zod": "^3.23.8" - }, - "engines": { - "node": ">=20.0" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/MaskingTechnology/jitar.git" - }, - "bugs": { - "url": "https://github.com/MaskingTechnology/jitar/issues" - }, - "homepage": "https://jitar.dev", - "keywords": [ - "jitar", - "nodejs" - ] -} diff --git a/packages/server-nodejs/src/JitarServer.ts b/packages/server-nodejs/src/JitarServer.ts deleted file mode 100644 index 9c66ffe8..00000000 --- a/packages/server-nodejs/src/JitarServer.ts +++ /dev/null @@ -1,307 +0,0 @@ - -import express, { Express } from 'express'; -import { Server } from 'http'; -import { Logger } from 'tslog'; - -import { LocalGateway, LocalWorker, LocalRepository, Proxy, Runtime, RemoteClassLoader, ExecutionScopes, Standalone } from '@jitar/runtime'; -import { ClassLoader, Serializer, SerializerBuilder, ValueSerializer } from '@jitar/serialization'; - -import ServerOptions from './configuration/ServerOptions.js'; - -import RuntimeConfigurationLoader from './utils/RuntimeConfigurationLoader.js'; -import RuntimeConfigurator from './utils/RuntimeConfigurator.js'; -import ServerOptionsReader from './utils/ServerOptionsReader.js'; - -import AssetsController from './controllers/AssetsController.js'; -import HealthController from './controllers/HealthController.js'; -import JitarController from './controllers/JitarController.js'; -import ModulesController from './controllers/ModulesController.js'; -import WorkerController from './controllers/WorkerController.js'; -import ProceduresController from './controllers/ProceduresController.js'; -import ProxyController from './controllers/ProxyController.js'; -import RPCController from './controllers/RPCController.js'; - -import RuntimeConfiguration from './configuration/RuntimeConfiguration.js'; -import RuntimeDefaults from './definitions/RuntimeDefaults.js'; - -import RuntimeNotAvailable from './errors/RuntimeNotAvailable.js'; -import LogBuilder from './utils/LogBuilder.js'; -import Headers from './definitions/Headers.js'; - -const STARTUP_MESSAGE = ` - ██ ██ ████████ █████ ██████ - ██ ██ ██ ██ ██ ██ ██ - ██ ██ ██ ███████ ██████ - ██ ██ ██ ██ ██ ██ ██ ██ - █████ ██ ██ ██ ██ ██ ██ - ____________________________________ - By Masking Technology (masking.tech) -`; - -export default class JitarServer -{ - #app: Express; - #server?: Server; - - #runtime?: Runtime; - #serializer: Serializer; - #classLoader: ClassLoader; - - #options: ServerOptions; - #configuration: RuntimeConfiguration; - #logger: Logger; - - constructor() - { - this.#classLoader = new RemoteClassLoader(); - this.#serializer = SerializerBuilder.build(this.#classLoader); - - this.#options = ServerOptionsReader.read(); - this.#configuration = RuntimeConfigurationLoader.load(this.#options.config); - this.#logger = LogBuilder.build(this.#options.loglevel); - - this.#app = express(); - - this.#app.use(express.json({limit: this.#options.bodylimit })); - this.#app.use(express.urlencoded({ extended: true })); - this.#app.use((request, response, next) => this.#addDefaultHeaders(request, response, next)); - - this.#app.disable('x-powered-by'); - - this.#printStartupMessage(); - } - - get classLoader(): ClassLoader - { - return this.#classLoader; - } - - async build(): Promise - { - this.#runtime = await RuntimeConfigurator.configure(this.#configuration); - this.#addControllers(); - } - - async start(): Promise - { - const url = new URL(this.#configuration.url ?? RuntimeDefaults.URL); - - try - { - await this.#startApplication(); - await this.#startServer(url.port); - } - catch (error: unknown) - { - const message = error instanceof Error ? error.message : String(error); - - this.#logger.error(`Failed to start server -> ${message}`); - - await this.stop(); - - return; - } - - this.#printProcedureInfo(); - - this.#logger.info(`Server started and listening at port ${url.port}`); - } - - async stop(): Promise - { - try - { - await this.#stopServer(); - await this.#stopApplication(); - } - catch (error: unknown) - { - const message = error instanceof Error ? error.message : String(error); - - this.#logger.error(`Caught error while stopping server -> ${message}`); - - return; - } - - this.#logger.info('Server stopped'); - } - - addSerializer(serializer: ValueSerializer): void - { - this.#serializer.addSerializer(serializer); - } - - #getRuntime(): Runtime - { - if (this.#runtime === undefined) - { - throw new RuntimeNotAvailable(); - } - - return this.#runtime; - } - - #addControllers(): void - { - if (this.#configuration.standalone !== undefined && this.#runtime instanceof Standalone) - { - const index = this.#configuration.standalone.index ?? RuntimeDefaults.INDEX; - const serveIndexOnNotFound = this.#configuration.standalone.serveIndexOnNotFound ?? RuntimeDefaults.SERVE_INDEX_ON_NOT_FOUND; - - this.#addStandAloneControllers(this.#runtime, index, serveIndexOnNotFound); - } - else if (this.#configuration.repository !== undefined && this.#runtime instanceof LocalRepository) - { - const index = this.#configuration.repository.index ?? RuntimeDefaults.INDEX; - const serveIndexOnNotFound = this.#configuration.repository.serveIndexOnNotFound ?? RuntimeDefaults.SERVE_INDEX_ON_NOT_FOUND; - - this.#addRepositoryControllers(this.#runtime, index, serveIndexOnNotFound); - } - else if (this.#configuration.gateway !== undefined && this.#runtime instanceof LocalGateway) - { - this.#addGatewayControllers(this.#runtime); - } - else if (this.#configuration.worker !== undefined && this.#runtime instanceof LocalWorker) - { - this.#addWorkerControllers(this.#runtime); - } - else if (this.#configuration.proxy !== undefined && this.#runtime instanceof Proxy) - { - this.#addProxyControllers(this.#runtime); - } - } - - #addStandAloneControllers(standalone: Standalone, index: string, serveIndexOnNotFound: boolean): void - { - new HealthController(this.#app, standalone, this.#logger); - new JitarController(this.#app); - new ModulesController(this.#app, standalone, this.#serializer, this.#logger); - new ProceduresController(this.#app, standalone, this.#logger); - new RPCController(this.#app, standalone, this.#serializer, this.#logger); - new AssetsController(this.#app, standalone, index, serveIndexOnNotFound, this.#logger); - } - - #addRepositoryControllers(repository: LocalRepository, index: string, serveIndexOnNotFound: boolean): void - { - new JitarController(this.#app); - new ModulesController(this.#app, repository, this.#serializer, this.#logger); - new AssetsController(this.#app, repository, index, serveIndexOnNotFound, this.#logger); - } - - #addGatewayControllers(gateway: LocalGateway): void - { - new WorkerController(this.#app, gateway, this.#logger); - new ProceduresController(this.#app, gateway, this.#logger); - new RPCController(this.#app, gateway, this.#serializer, this.#logger); - } - - #addWorkerControllers(worker: LocalWorker): void - { - new HealthController(this.#app, worker, this.#logger); - new ProceduresController(this.#app, worker, this.#logger); - new RPCController(this.#app, worker, this.#serializer, this.#logger); - } - - #addProxyControllers(proxy: Proxy): void - { - new ProxyController(this.#app, proxy, this.#logger); - } - - async #startApplication(): Promise - { - const runtime = this.#getRuntime(); - - await runtime.start(); - - const setUpScripts = this.#configuration.setUp; - - if (setUpScripts === undefined) - { - return; - } - - for (const setUpScript of setUpScripts) - { - await runtime.import(setUpScript, ExecutionScopes.APPLICATION); - } - } - - async #stopApplication(): Promise - { - const runtime = this.#getRuntime(); - - await runtime.stop(); - - const tearDownScripts = this.#configuration.tearDown; - - if (tearDownScripts === undefined) - { - return; - } - - for (const tearDownScript of tearDownScripts) - { - await runtime.import(tearDownScript, ExecutionScopes.APPLICATION); - } - } - - #startServer(port: string): Promise - { - if (this.#server !== undefined) - { - return Promise.resolve(); - } - - return new Promise((resolve, reject) => - { - this.#server = this.#app.listen(port, resolve); - - this.#server.on('error', reject); - }); - } - - #stopServer(): Promise - { - if (this.#server === undefined) - { - return Promise.resolve(); - } - - return new Promise(resolve => { this.#server?.close(() => resolve()); }); - } - - #printStartupMessage(): void - { - console.log(STARTUP_MESSAGE); - } - - #printProcedureInfo() - { - const runtime = this.#getRuntime() as LocalWorker | Standalone; - - if (runtime instanceof LocalWorker === false - && runtime instanceof Standalone === false) - { - return; - } - - const procedureNames = runtime.getProcedureNames(); - - if (procedureNames.length === 0) - { - return; - } - - procedureNames.sort(); - - this.#logger.info('Registered RPC entries', procedureNames); - } - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - #addDefaultHeaders(request: express.Request, response: express.Response, next: express.NextFunction): void - { - response.setHeader(Headers.CONTENT_TYPE_OPTIONS, 'nosniff'); - - next(); - } -} diff --git a/packages/server-nodejs/src/configuration/GatewayConfiguration.ts b/packages/server-nodejs/src/configuration/GatewayConfiguration.ts deleted file mode 100644 index 8fc50519..00000000 --- a/packages/server-nodejs/src/configuration/GatewayConfiguration.ts +++ /dev/null @@ -1,36 +0,0 @@ - -import { z } from 'zod'; - -import ProcedureRuntimeConfiguration from './ProcedureRuntimeConfiguration'; - -export const gatewaySchema = z - .object({ - repository: z.string().url(), - middlewares: z.array(z.string()).optional(), - monitor: z.number().optional(), - trustKey: z.string().optional() - }) - .strict() - .transform((value) => new GatewayConfiguration(value.repository, value.middlewares, value.monitor, value.trustKey)); - -export default class GatewayConfiguration extends ProcedureRuntimeConfiguration -{ - #monitor?: number; - #repository: string; - #trustKey?: string; - - constructor(repository: string, middlewares?: string[], monitor?: number, trustKey?: string) - { - super(middlewares); - - this.#monitor = monitor; - this.#repository = repository; - this.#trustKey = trustKey; - } - - get monitor() { return this.#monitor; } - - get repository() { return this.#repository; } - - get trustKey() { return this.#trustKey; } -} diff --git a/packages/server-nodejs/src/configuration/ProcedureRuntimeConfiguration.ts b/packages/server-nodejs/src/configuration/ProcedureRuntimeConfiguration.ts deleted file mode 100644 index 2b6b0ad3..00000000 --- a/packages/server-nodejs/src/configuration/ProcedureRuntimeConfiguration.ts +++ /dev/null @@ -1,12 +0,0 @@ - -export default class ProcedureRuntimeConfiguration -{ - #middlewares?: string[]; - - constructor(middlewares?: string[]) - { - this.#middlewares = middlewares; - } - - get middlewares() { return this.#middlewares; } -} diff --git a/packages/server-nodejs/src/configuration/ProxyConfiguration.ts b/packages/server-nodejs/src/configuration/ProxyConfiguration.ts deleted file mode 100644 index fd977138..00000000 --- a/packages/server-nodejs/src/configuration/ProxyConfiguration.ts +++ /dev/null @@ -1,57 +0,0 @@ - -import { z } from 'zod'; - -import ProcedureRuntimeConfiguration from './ProcedureRuntimeConfiguration'; - -export const proxySchema = z - .object({ - worker: z.string().url().optional(), - gateway: z.string().url().optional(), - repository: z.string().url(), - middlewares: z.array(z.string()).optional() - }) - .strict() - .superRefine((value, ctx) => - { - if (value.worker === undefined && value.gateway === undefined) - { - ctx.addIssue({ - code: z.ZodIssueCode.custom, - message: 'Either worker or gateway must be defined', - path: ['worker', 'gateway'] - }); - } - - if (value.worker !== undefined && value.gateway !== undefined) - { - ctx.addIssue({ - code: z.ZodIssueCode.custom, - message: 'Only worker or gateway must be defined', - path: ['worker', 'gateway'], - - }); - } - }) - .transform((value) => new ProxyConfiguration(value.worker, value.gateway, value.repository, value.middlewares)); - -export default class ProxyConfiguration extends ProcedureRuntimeConfiguration -{ - #worker?: string; - #gateway?: string; - #repository: string; - - constructor(worker: string | undefined, gateway: string | undefined, repository: string, middlewares?: string[]) - { - super(middlewares); - - this.#worker = worker; - this.#gateway = gateway; - this.#repository = repository; - } - - get worker() { return this.#worker; } - - get gateway() { return this.#gateway; } - - get repository() { return this.#repository; } -} diff --git a/packages/server-nodejs/src/configuration/RepositoryConfiguration.ts b/packages/server-nodejs/src/configuration/RepositoryConfiguration.ts deleted file mode 100644 index b984f89e..00000000 --- a/packages/server-nodejs/src/configuration/RepositoryConfiguration.ts +++ /dev/null @@ -1,46 +0,0 @@ - -import { z } from 'zod'; - -export const repositorySchema = z - .object({ - source: z.string().optional(), - cache: z.string().optional(), - index: z.string().optional(), - serveIndexOnNotFound: z.boolean().optional(), - assets: z.array(z.string()).optional(), - overrides: z.record(z.string(), z.string()).optional(), - }) - .strict() - .transform((value) => new RepositoryConfiguration(value.source, value.cache, value.index, value.serveIndexOnNotFound, value.assets, value.overrides)); - -export default class RepositoryConfiguration -{ - #source?: string; - #cache?: string; - #index?: string; - #serveIndexOnNotFound?: boolean; - #assets?: string[]; - #overrides?: Record; - - constructor(source?: string, cache?: string, index?: string, serveIndexOnNotFound?: boolean, assets?: string[], overrides?: Record) - { - this.#source = source; - this.#cache = cache; - this.#index = index; - this.#serveIndexOnNotFound = serveIndexOnNotFound; - this.#assets = assets; - this.#overrides = overrides; - } - - get source() { return this.#source; } - - get cache() { return this.#cache; } - - get index() { return this.#index; } - - get serveIndexOnNotFound() { return this.#serveIndexOnNotFound; } - - get assets() { return this.#assets; } - - get overrides() { return this.#overrides; } -} diff --git a/packages/server-nodejs/src/configuration/RuntimeConfiguration.ts b/packages/server-nodejs/src/configuration/RuntimeConfiguration.ts deleted file mode 100644 index 1482978a..00000000 --- a/packages/server-nodejs/src/configuration/RuntimeConfiguration.ts +++ /dev/null @@ -1,67 +0,0 @@ - -import { z } from 'zod'; - -import GatewayConfiguration, { gatewaySchema } from './GatewayConfiguration.js'; -import WorkerConfiguration, { workerSchema } from './WorkerConfiguration.js'; -import ProxyConfiguration, { proxySchema } from './ProxyConfiguration.js'; -import RepositoryConfiguration, { repositorySchema } from './RepositoryConfiguration.js'; -import StandaloneConfiguration, { standaloneSchema } from './StandaloneConfiguration.js'; - -export const runtimeSchema = z - .object({ - url: z.string().optional(), - setUp: z.array(z.string()).optional(), - tearDown: z.array(z.string()).optional(), - healthChecks: z.array(z.string()).optional(), - standalone: standaloneSchema.optional(), - repository: repositorySchema.optional(), - gateway: gatewaySchema.optional(), - worker: workerSchema.optional(), - proxy: proxySchema.optional() - }) - .strict() - .transform((value) => new RuntimeConfiguration(value.url, value.setUp, value.tearDown, value.healthChecks, value.standalone, value.repository, value.gateway, value.worker, value.proxy)); - -export default class RuntimeConfiguration -{ - #url?: string; - #setUp?: string[]; - #tearDown?: string[]; - #healthChecks?: string[]; - #standalone?: StandaloneConfiguration; - #repository?: RepositoryConfiguration; - #gateway?: GatewayConfiguration; - #worker?: WorkerConfiguration; - #proxy?: ProxyConfiguration; - - constructor(url?: string, setUp?: string[], tearDown?: string[], healthChecks?: string[], standalone?: StandaloneConfiguration, repository?: RepositoryConfiguration, gateway?: GatewayConfiguration, worker?: WorkerConfiguration, proxy?: ProxyConfiguration) - { - this.#url = url; - this.#setUp = setUp; - this.#tearDown = tearDown; - this.#healthChecks = healthChecks; - this.#standalone = standalone; - this.#repository = repository; - this.#gateway = gateway; - this.#worker = worker; - this.#proxy = proxy; - } - - get url() { return this.#url; } - - get setUp() { return this.#setUp; } - - get tearDown() { return this.#tearDown; } - - get healthChecks() { return this.#healthChecks; } - - get standalone() { return this.#standalone; } - - get repository() { return this.#repository; } - - get gateway() { return this.#gateway; } - - get worker() { return this.#worker; } - - get proxy() { return this.#proxy; } -} diff --git a/packages/server-nodejs/src/configuration/ServerOptions.ts b/packages/server-nodejs/src/configuration/ServerOptions.ts deleted file mode 100644 index b69f5b2a..00000000 --- a/packages/server-nodejs/src/configuration/ServerOptions.ts +++ /dev/null @@ -1,33 +0,0 @@ - -import { z } from 'zod'; -import { LogLevel } from '../utils/LogBuilder.js'; - -const DEFAULT_BODY_LIMIT = 1024 * 200; - -export const serverOptionsSchema = z - .object({ - loglevel: z.nativeEnum(LogLevel).optional(), - config: z.string().endsWith('.json'), - bodylimit: z.number().positive().optional() - }) - .transform((value) => new ServerOptions(value.config, value.loglevel, value.bodylimit)); - -export default class ServerOptions -{ - #config: string; - #loglevel: string; - #bodylimit: number; - - constructor(config: string, loglevel = 'info', bodylimit = DEFAULT_BODY_LIMIT) - { - this.#config = config; - this.#loglevel = loglevel; - this.#bodylimit = bodylimit; - } - - get config() { return this.#config; } - - get loglevel() { return this.#loglevel; } - - get bodylimit() { return this.#bodylimit; } -} diff --git a/packages/server-nodejs/src/configuration/StandaloneConfiguration.ts b/packages/server-nodejs/src/configuration/StandaloneConfiguration.ts deleted file mode 100644 index 916e72e9..00000000 --- a/packages/server-nodejs/src/configuration/StandaloneConfiguration.ts +++ /dev/null @@ -1,61 +0,0 @@ - -import { z } from 'zod'; - -import ProcedureRuntimeConfiguration from './ProcedureRuntimeConfiguration'; - -export const standaloneSchema = z - .object({ - source: z.string().optional(), - cache: z.string().optional(), - index: z.string().optional(), - serveIndexOnNotFound: z.boolean().optional(), - segments: z.array(z.string()).optional(), - assets: z.array(z.string()).optional(), - middlewares: z.array(z.string()).optional(), - overrides: z.record(z.string(), z.string()).optional(), - trustKey: z.string().optional() - }) - .strict() - .transform((value) => new StandaloneConfiguration(value.source, value.cache, value.index, value.serveIndexOnNotFound, value.segments, value.assets, value.middlewares, value.overrides, value.trustKey)); - -export default class StandaloneConfiguration extends ProcedureRuntimeConfiguration -{ - #source?: string; - #cache?: string; - #index?: string; - #serveIndexOnNotFound?: boolean; - #segments?: string[]; - #assets?: string[]; - #overrides?: Record; - #trustKey?: string; - - constructor(source?: string, cache?: string, index?: string, serveIndexOnNotFound?: boolean, segments?: string[], assets?: string[], middlewares?: string[], overrides?: Record, trustKey?: string) - { - super(middlewares); - - this.#source = source; - this.#cache = cache; - this.#index = index; - this.#serveIndexOnNotFound = serveIndexOnNotFound; - this.#segments = segments; - this.#assets = assets; - this.#overrides = overrides; - this.#trustKey = trustKey; - } - - get source() { return this.#source; } - - get cache() { return this.#cache; } - - get index() { return this.#index; } - - get serveIndexOnNotFound() { return this.#serveIndexOnNotFound; } - - get segments() { return this.#segments; } - - get assets() { return this.#assets; } - - get overrides() { return this.#overrides; } - - get trustKey() { return this.#trustKey; } -} diff --git a/packages/server-nodejs/src/configuration/WorkerConfiguration.ts b/packages/server-nodejs/src/configuration/WorkerConfiguration.ts deleted file mode 100644 index 278e0ef2..00000000 --- a/packages/server-nodejs/src/configuration/WorkerConfiguration.ts +++ /dev/null @@ -1,41 +0,0 @@ - -import { z } from 'zod'; - -import ProcedureRuntimeConfiguration from './ProcedureRuntimeConfiguration'; - -export const workerSchema = z - .object({ - gateway: z.string().url().optional(), - repository: z.string().url().optional(), - segments: z.array(z.string()).nonempty(), - middlewares: z.array(z.string()).optional(), - trustKey: z.string().optional() - }) - .strict() - .transform((value) => new WorkerConfiguration(value.gateway, value.repository, value.segments, value.middlewares, value.trustKey)); - -export default class WorkerConfiguration extends ProcedureRuntimeConfiguration -{ - #gateway?: string; - #repository?: string; - #segments: string[]; - #trustKey?: string; - - constructor(gateway: string | undefined, repository: string | undefined, segments: string[], middlewares?: string[], trustKey?: string) - { - super(middlewares); - - this.#gateway = gateway; - this.#repository = repository; - this.#segments = segments; - this.#trustKey = trustKey; - } - - get gateway() { return this.#gateway; } - - get repository() { return this.#repository; } - - get segments() { return this.#segments; } - - get trustKey() { return this.#trustKey; } -} diff --git a/packages/server-nodejs/src/controllers/AssetsController.ts b/packages/server-nodejs/src/controllers/AssetsController.ts deleted file mode 100644 index 0aaabdf8..00000000 --- a/packages/server-nodejs/src/controllers/AssetsController.ts +++ /dev/null @@ -1,77 +0,0 @@ - -import { Application, Request, Response } from 'express'; -import { Logger } from 'tslog'; - -import { LocalRepository, Standalone, FileNotFound } from '@jitar/runtime'; - -import Headers from '../definitions/Headers.js'; -import ContentTypes from '../definitions/ContentTypes.js'; - -type Repository = LocalRepository | Standalone; - -export default class AssetsController -{ - #repository: Repository; - #indexFile: string; - #serveIndexOnNotFound: boolean; - #logger: Logger; - - constructor(app: Application, repository: Repository, indexFile: string, serveIndexOnNotFound: boolean, logger: Logger) - { - this.#repository = repository; - this.#indexFile = indexFile; - this.#serveIndexOnNotFound = serveIndexOnNotFound; - this.#logger = logger; - - app.get('*', (request: Request, response: Response) => { this.#getContent(request, response); }); - } - - async #getContent(request: Request, response: Response): Promise - { - const path = request.path.substring(1).trim(); - const decodedPath = decodeURIComponent(path); - const filename = decodedPath.length === 0 ? this.#indexFile : decodedPath; - - this.#loadContent(filename, response); - } - - async #loadContent(filename: string, response: Response): Promise - { - try - { - const file = await this.#repository.readAsset(filename); - - this.#logger.info(`Got asset -> '${filename}'`); - - if (file.type === ContentTypes.HTML) - { - response.setHeader(Headers.FRAME_OPTIONS, 'DENY'); - } - - response.setHeader(Headers.CONTENT_TYPE, file.type); - response.status(200).send(file.content); - } - catch (error: unknown) - { - if (error instanceof FileNotFound) - { - if (this.#serveIndexOnNotFound && filename !== this.#indexFile) - { - return this.#loadContent(this.#indexFile, response); - } - - this.#logger.warn(`Failed to get asset -> '${filename}'`); - - response.status(404).type('text').send('Not found'); - - return; - } - - const message = error instanceof Error ? error.message : 'Internal server error'; - - this.#logger.error(`Failed to get asset -> ${message}`); - - response.status(500).type('text').send(message); - } - } -} diff --git a/packages/server-nodejs/src/controllers/HealthController.ts b/packages/server-nodejs/src/controllers/HealthController.ts deleted file mode 100644 index 30425210..00000000 --- a/packages/server-nodejs/src/controllers/HealthController.ts +++ /dev/null @@ -1,43 +0,0 @@ - -import express, { Request, Response } from 'express'; -import { Logger } from 'tslog'; - -import { LocalWorker, Standalone } from '@jitar/runtime'; -import Headers from '../definitions/Headers'; -import ContentTypes from '../definitions/ContentTypes'; - -export default class HealthController -{ - #worker: LocalWorker | Standalone; - #logger: Logger; - - constructor(app: express.Application, worker: LocalWorker | Standalone, logger: Logger) - { - this.#worker = worker; - this.#logger = logger; - - app.get('/health', (request: Request, response: Response) => { this.getHealth(request, response); }); - app.get('/health/status', (request: Request, response: Response) => { this.isHealthy(request, response); }); - } - - async getHealth(request: Request, response: Response): Promise - { - const health = await this.#worker.getHealth(); - const data = Object.fromEntries(health); - - this.#logger.debug('Got health'); - - return response.status(200).send(data); - } - - async isHealthy(request: Request, response: Response): Promise - { - const healthy = await this.#worker.isHealthy(); - - this.#logger.debug('Got health status'); - - response.setHeader(Headers.CONTENT_TYPE, ContentTypes.TEXT); - - return response.status(200).send(healthy); - } -} diff --git a/packages/server-nodejs/src/controllers/JitarController.ts b/packages/server-nodejs/src/controllers/JitarController.ts deleted file mode 100644 index bf352e8e..00000000 --- a/packages/server-nodejs/src/controllers/JitarController.ts +++ /dev/null @@ -1,15 +0,0 @@ - -import express from 'express'; -import path from 'path'; -import { fileURLToPath } from 'url'; - -const filePath = fileURLToPath(import.meta.url); -const fileLocation = path.dirname(filePath); - -export default class JitarController -{ - constructor(app: express.Application) - { - app.use('/jitar', express.static(fileLocation)); - } -} diff --git a/packages/server-nodejs/src/controllers/ModulesController.ts b/packages/server-nodejs/src/controllers/ModulesController.ts deleted file mode 100644 index 88307484..00000000 --- a/packages/server-nodejs/src/controllers/ModulesController.ts +++ /dev/null @@ -1,95 +0,0 @@ - -import express, { Request, Response } from 'express'; -import { Logger } from 'tslog'; - -import { ClientIdHelper, LocalRepository, Standalone } from '@jitar/runtime'; -import { Serializer } from '@jitar/serialization'; - -import Headers from '../definitions/Headers'; -import ContentTypes from '../definitions/ContentTypes'; - -const clientIdHelper = new ClientIdHelper(); - -export default class ModulesController -{ - #repository: LocalRepository | Standalone; - #serializer: Serializer; - #logger: Logger; - - constructor(app: express.Application, repository: LocalRepository | Standalone, serializer: Serializer, logger: Logger) - { - this.#repository = repository; - this.#serializer = serializer; - this.#logger = logger; - - app.post('/modules', (request: Request, response: Response) => { this.registerClient(request, response); }); - app.get('/modules/:clientId/*', (request: Request, response: Response) => { this.getModule(request, response); }); - } - - async registerClient(request: Request, response: Response): Promise - { - this.#logger.info('Register client'); - - if ((request.body instanceof Array) === false) - { - return response.status(400).send('Invalid segment file list.'); - } - - const segmentFiles = request.body as string[]; - - for (const segmentFile of segmentFiles) - { - if (typeof segmentFile !== 'string') - { - return response.status(400).send('Invalid segment file list.'); - } - } - - const clientId = await this.#repository.registerClient(segmentFiles); - - this.#logger.info(`Registered client -> ${clientId} [${segmentFiles.join(',')}]`); - - response.setHeader(Headers.CONTENT_TYPE, ContentTypes.TEXT); - - return response.status(200).send(clientId); - } - - async getModule(request: Request, response: Response): Promise - { - const clientId = request.params.clientId; - - if (typeof clientId !== 'string' || clientIdHelper.validate(clientId) === false) - { - return response.status(400).send('Invalid client id.'); - } - - this.#logger.info(`Get module for -> '${request.params.clientId}'`); - - const pathKey = `/${clientId}/`; - const pathIndex = request.path.indexOf(pathKey) + pathKey.length; - const filename = request.path.substring(pathIndex); - - try - { - const file = await this.#repository.readModule(filename, clientId); - - this.#logger.info(`Got module -> '${filename}' (${clientId})`); - - response.setHeader(Headers.CONTENT_TYPE, file.type); - - return response.status(200).send(file.content); - } - catch (error: unknown) - { - const message = error instanceof Error ? error.message : String(error); - - this.#logger.error(`Failed to get module -> '${filename}' (${clientId}) | ${message}`); - - const data = this.#serializer.serialize(error); - - response.setHeader(Headers.CONTENT_TYPE, ContentTypes.JSON); - - return response.status(500).send(data); - } - } -} diff --git a/packages/server-nodejs/src/controllers/ProceduresController.ts b/packages/server-nodejs/src/controllers/ProceduresController.ts deleted file mode 100644 index 7c3dac9e..00000000 --- a/packages/server-nodejs/src/controllers/ProceduresController.ts +++ /dev/null @@ -1,33 +0,0 @@ - -import express, { Request, Response } from 'express'; -import { Logger } from 'tslog'; - -import { LocalGateway, LocalWorker, Standalone } from '@jitar/runtime'; - -import Headers from '../definitions/Headers'; -import ContentTypes from '../definitions/ContentTypes'; - -export default class ProceduresController -{ - #runtime: LocalGateway | LocalWorker | Standalone; - #logger: Logger; - - constructor(app: express.Application, runtime: LocalGateway | LocalWorker | Standalone, logger: Logger) - { - this.#runtime = runtime; - this.#logger = logger; - - app.get('/procedures', (request: Request, response: Response) => { this.getProcedureNames(request, response); }); - } - - async getProcedureNames(request: Request, response: Response): Promise - { - const names = this.#runtime.getProcedureNames(); - - this.#logger.info('Got procedure names'); - - response.setHeader(Headers.CONTENT_TYPE, ContentTypes.JSON); - - return response.status(200).send(names); - } -} diff --git a/packages/server-nodejs/src/controllers/ProxyController.ts b/packages/server-nodejs/src/controllers/ProxyController.ts deleted file mode 100644 index 4aa1de06..00000000 --- a/packages/server-nodejs/src/controllers/ProxyController.ts +++ /dev/null @@ -1,36 +0,0 @@ - -import express from 'express'; -import expressProxy from 'express-http-proxy'; -import { IncomingMessage } from 'http'; -import { Logger } from 'tslog'; - -import { Proxy } from '@jitar/runtime'; - -export default class ProxyController -{ - #logger: Logger; - - #repositoryUrl: string; - #runnerUrl: string; - - constructor(app: express.Application, proxy: Proxy, logger: Logger) - { - this.#logger = logger; - - this.#repositoryUrl = proxy.repository.url ?? ''; - this.#runnerUrl = proxy.runner.url ?? ''; - - app.use('/', expressProxy((message: IncomingMessage): string => this.#selectProxy(message))); - } - - #selectProxy(message: IncomingMessage): string - { - const url = message.url ?? ''; - - this.#logger.info(`Forwarding -> ${url}`); - - return url.startsWith('/rpc') - ? this.#runnerUrl - : this.#repositoryUrl; - } -} diff --git a/packages/server-nodejs/src/controllers/RPCController.ts b/packages/server-nodejs/src/controllers/RPCController.ts deleted file mode 100644 index 352e551e..00000000 --- a/packages/server-nodejs/src/controllers/RPCController.ts +++ /dev/null @@ -1,343 +0,0 @@ - -import express, { Request as ExpressRequest, Response as ExpressResponse } from 'express'; -import { Logger } from 'tslog'; - -import { Request as JitarRequest, Version, VersionParser, ProcedureRuntime, BadRequest, Unauthorized, PaymentRequired, Forbidden, NotFound, Teapot, NotImplemented } from '@jitar/runtime'; -import { Serializer } from '@jitar/serialization'; - -import CorsMiddleware from '../middleware/CorsMiddleware.js'; -import Headers from '../definitions/Headers.js'; -import ContentTypes from '../definitions/ContentTypes.js'; - -const RPC_PARAMETERS = ['version', 'serialize']; -const IGNORED_HEADER_KEYS = ['host', 'connection', 'content-length', 'accept-encoding', 'user-agent', 'keep-alive']; -const CORS_MAX_AGE = 86400; - -// Required to work after minification. -const BAD_REQUEST_NAME = BadRequest.name; -const UNAUTHORIZED_NAME = Unauthorized.name; -const PAYMENT_REQUIRED_NAME = PaymentRequired.name; -const FORBIDDEN_NAME = Forbidden.name; -const NOT_FOUND_NAME = NotFound.name; -const TEAPOT_NAME = Teapot.name; -const NOT_IMPLEMENTED_NAME = NotImplemented.name; - -export default class RPCController -{ - #runtime: ProcedureRuntime; - #serializer: Serializer; - #logger: Logger; - - constructor(app: express.Application, runtime: ProcedureRuntime, serializer: Serializer, logger: Logger) - { - this.#runtime = runtime; - this.#serializer = serializer; - this.#logger = logger; - - app.get('/rpc/*', (request: ExpressRequest, response: ExpressResponse) => { this.runGet(request, response); }); - app.post('/rpc/*', (request: ExpressRequest, response: ExpressResponse) => { this.runPost(request, response); }); - app.options('/rpc/*', (request: ExpressRequest, response: ExpressResponse) => { this.runOptions(request, response); }); - } - - async runGet(request: ExpressRequest, response: ExpressResponse): Promise - { - try - { - const fqn = this.#extractFqn(request); - const version = this.#extractVersion(request); - const args = this.#extractQueryArguments(request); - const headers = this.#extractHeaders(request); - const serialize = this.#extractSerialize(request); - - return this.#run(fqn, version, args, headers, response, serialize); - } - catch (error: unknown) - { - const message = error instanceof Error ? error.message : String(error); - - this.#logger.warn(`Invalid request -> ${message}`); - - return response.status(400).type('text').send(`Invalid request -> ${message}`); - } - } - - async runPost(request: ExpressRequest, response: ExpressResponse): Promise - { - try - { - const fqn = this.#extractFqn(request); - const version = this.#extractVersion(request); - const args = this.#extractBodyArguments(request); - const headers = this.#extractHeaders(request); - const serialize = this.#extractSerialize(request); - - return this.#run(fqn, version, args, headers, response, serialize); - } - catch (error: unknown) - { - const message = error instanceof Error ? error.message : String(error); - - this.#logger.warn(`Invalid request -> ${message}`); - - return response.status(400).type('text').send(`Invalid request -> ${message}`); - } - } - - async runOptions(request: ExpressRequest, response: ExpressResponse): Promise - { - return this.#setCors(response); - } - - #extractFqn(request: ExpressRequest): string - { - const decodedFqn = decodeURIComponent(request.path.trim()); - const fqn = decodedFqn.substring(5).trim(); - - if (fqn.length === 0) - { - throw new BadRequest('Missing procedure name'); - } - - if (fqn.includes('..')) - { - throw new BadRequest('Invalid procedure name'); - } - - return fqn; - } - - #extractVersion(request: ExpressRequest): Version - { - const version = request.query.version !== undefined - ? request.query.version - : ''; - - if (typeof version !== 'string') - { - throw new BadRequest('Invalid version number'); - } - - return VersionParser.parse(version); - } - - #extractSerialize(request: ExpressRequest): boolean - { - return request.query.serialize === 'true'; - } - - #extractQueryArguments(request: ExpressRequest): Record - { - const args: Record = {}; - - for (const [key, value] of Object.entries(request.query)) - { - if (RPC_PARAMETERS.includes(key)) - { - // We need to filter out the RPC parameters, - // because they are not a procedure argument. - - continue; - } - - args[key] = value; - } - - return args; - } - - #extractBodyArguments(request: ExpressRequest): Record - { - return request.body; - } - - #extractHeaders(request: ExpressRequest): Map - { - const headers = new Map(); - - for (const [key, value] of Object.entries(request.headers)) - { - if (value === undefined) - { - continue; - } - - const lowerKey = key.toLowerCase(); - const stringValue = value.toString(); - - if (IGNORED_HEADER_KEYS.includes(lowerKey)) - { - continue; - } - - headers.set(lowerKey, stringValue); - } - - return headers; - } - - async #run(fqn: string, version: Version, args: Record, headers: Map, response: ExpressResponse, serialize: boolean): Promise - { - if (this.#runtime.hasProcedure(fqn) === false) - { - // We need this check to make sure we won't run an private procedure. - response.setHeader(Headers.CONTENT_TYPE, ContentTypes.TEXT); - - return response.status(404).send(`Procedure not found -> ${fqn}`); - } - - try - { - const deserializedArgs = await this.#serializer.deserialize(args) as Record; - const argsMap = new Map(Object.entries(deserializedArgs)); - - const runtimeRequest = new JitarRequest(fqn, version, argsMap, headers); - const runtimeResponse = await this.#runtime.handle(runtimeRequest); - - this.#logger.info(`Ran procedure -> ${fqn} (v${version.toString()})`); - - this.#setResponseHeaders(response, runtimeResponse.headers); - - return this.#createResultResponse(runtimeResponse.result, response, serialize); - } - catch (error: unknown) - { - // When using the RPC API we need to return a human readable error message by default. - // Only when a serialized result is requested we can return the error object (used by the remote). - - const message = error instanceof Error ? error.message : String(error); - const errorData = serialize ? error : message; - - this.#logger.error(`Failed to run procedure -> ${fqn} (v${version.toString()}) | ${message}`); - - return this.#createErrorResponse(error, errorData, response, serialize); - } - } - - async #setCors(response: ExpressResponse): Promise - { - const cors = this.#runtime.getMiddleware(CorsMiddleware) as CorsMiddleware; - - if (cors === undefined) - { - return response.status(204).send(); - } - - response.setHeader('Access-Control-Allow-Origin', cors.allowOrigin); - response.setHeader('Access-Control-Allow-Methods', cors.allowMethods); - response.setHeader('Access-Control-Allow-Headers', cors.allowHeaders); - response.setHeader('Access-Control-Max-Age', CORS_MAX_AGE); - - return response.status(204).send(); - } - - async #createResultResponse(result: unknown, response: ExpressResponse, serialize: boolean): Promise - { - const content = await this.#createResponseContent(result, serialize); - const contentType = this.#createResponseContentType(content); - const responseContent = contentType === ContentTypes.JSON ? content : String(content); - const statusCode = this.#createResponseResultStatusCode(result, response); - - response.setHeader(Headers.CONTENT_TYPE, contentType); - - return response.status(statusCode).send(responseContent); - } - - async #createErrorResponse(error: unknown, errorData: unknown, response: ExpressResponse, serialize: boolean): Promise - { - const content = await this.#createResponseContent(errorData, serialize); - const contentType = this.#createResponseContentType(content); - const statusCode = this.#createResponseErrorStatusCode(error); - - response.setHeader(Headers.CONTENT_TYPE, contentType); - - return response.status(statusCode).send(content); - } - - async #createResponseContent(data: unknown, serialize: boolean): Promise - { - return serialize - ? this.#serializer.serialize(data) - : data; - } - - #createResponseContentType(content: unknown): string - { - switch(typeof content) - { - case 'boolean': return ContentTypes.BOOLEAN; - case 'number': return ContentTypes.NUMBER; - case 'object': return ContentTypes.JSON; - default: return ContentTypes.TEXT; - } - } - - #setResponseHeaders(response: ExpressResponse, headers: Map): void - { - for (const [key, value] of headers.entries()) - { - if (value === undefined) - { - continue; - } - - if (IGNORED_HEADER_KEYS.includes(key)) - { - continue; - } - - response.set(key, value); - } - } - - #createResponseResultStatusCode(result: unknown, response: ExpressResponse): number - { - if (response.hasHeader(Headers.LOCATION)) - { - return 302; - } - - if (result === undefined) - { - return 204; - } - - return 200; - } - - #createResponseErrorStatusCode(error: unknown): number - { - if (error instanceof Object === false) - { - return 500; - } - - const errorClass = error.constructor; - - if (this.#isClassType(errorClass, BAD_REQUEST_NAME)) return 400; - if (this.#isClassType(errorClass, UNAUTHORIZED_NAME)) return 401; - if (this.#isClassType(errorClass, PAYMENT_REQUIRED_NAME)) return 402; - if (this.#isClassType(errorClass, FORBIDDEN_NAME)) return 403; - if (this.#isClassType(errorClass, NOT_FOUND_NAME)) return 404; - if (this.#isClassType(errorClass, TEAPOT_NAME)) return 418; - if (this.#isClassType(errorClass, NOT_IMPLEMENTED_NAME)) return 501; - - return 500; - } - - #isClassType(clazz: Function, className: string): boolean - { - if (clazz.name === className) - { - return true; - } - - const parentClass = Object.getPrototypeOf(clazz); - - if (parentClass.name === '') - { - return false; - } - - return this.#isClassType(parentClass, className); - } -} diff --git a/packages/server-nodejs/src/controllers/WorkerController.ts b/packages/server-nodejs/src/controllers/WorkerController.ts deleted file mode 100644 index 634a2ef9..00000000 --- a/packages/server-nodejs/src/controllers/WorkerController.ts +++ /dev/null @@ -1,71 +0,0 @@ - -import express, { Request, Response } from 'express'; -import { Logger } from 'tslog'; - -import { LocalGateway, RemoteWorker } from '@jitar/runtime'; - -import WorkerDto, { workerDtoSchema } from '../models/WorkerDto.js'; -import DataConverter from '../utils/DataConverter.js'; -import Headers from '../definitions/Headers.js'; -import ContentTypes from '../definitions/ContentTypes.js'; -import ConversionError from '../errors/ConversionError.js'; - -export default class WorkerController -{ - #gateway: LocalGateway; - #logger: Logger; - - constructor(app: express.Application, gateway: LocalGateway, logger: Logger) - { - this.#gateway = gateway; - this.#logger = logger; - - app.get('/workers', (request: Request, response: Response) => { this.getWorkers(request, response); }); - app.post('/workers', (request: Request, response: Response) => { this.addWorker(request, response); }); - } - - async getWorkers(request: Request, response: Response): Promise - { - const workers = this.#gateway.workers.map(worker => { return { url: worker.url, procedureNames: worker.getProcedureNames() }; }); - - this.#logger.info('Got workers'); - - response.setHeader(Headers.CONTENT_TYPE, ContentTypes.JSON); - - return response.status(200).send(workers); - } - - async addWorker(request: Request, response: Response): Promise - { - try - { - const workerDto = DataConverter.convert(workerDtoSchema, request.body); - - const worker = new RemoteWorker(workerDto.url); - worker.procedureNames = new Set(workerDto.procedureNames); - - await this.#gateway.addWorker(worker, workerDto.trustKey); - - this.#logger.info(`Added worker -> ${worker.url}`); - - return response.status(201).send(); - } - catch (error: unknown) - { - if (error instanceof ConversionError) - { - const message = error.message; - - this.#logger.warn(`Failed to add worker | ${message}`); - - return response.status(400).type('text').send(message); - } - - const message = error instanceof Error ? error.message : 'Internal server error'; - - this.#logger.error(`Failed to add worker | ${message}`); - - return response.status(500).type('text').send(message); - } - } -} diff --git a/packages/server-nodejs/src/definitions/Headers.ts b/packages/server-nodejs/src/definitions/Headers.ts deleted file mode 100644 index ad80f705..00000000 --- a/packages/server-nodejs/src/definitions/Headers.ts +++ /dev/null @@ -1,12 +0,0 @@ - -const Headers = -{ - CONTENT_TYPE: 'Content-Type', - CONTENT_TYPE_OPTIONS: 'X-Content-Type-Options', - FRAME_OPTIONS : 'X-Frame-Options', - LOCATION: 'Location', -} as const; - -Object.freeze(Headers); - -export default Headers; diff --git a/packages/server-nodejs/src/definitions/RuntimeDefaults.ts b/packages/server-nodejs/src/definitions/RuntimeDefaults.ts deleted file mode 100644 index 1d2c6cba..00000000 --- a/packages/server-nodejs/src/definitions/RuntimeDefaults.ts +++ /dev/null @@ -1,13 +0,0 @@ - -const RuntimeDefaults = -{ - URL: 'http://localhost:3000', - SOURCE: './dist', - CACHE: './.jitar', - INDEX: 'index.html', - SERVE_INDEX_ON_NOT_FOUND: false -} as const; - -Object.freeze(RuntimeDefaults); - -export default RuntimeDefaults; diff --git a/packages/server-nodejs/src/errors/ConversionError.ts b/packages/server-nodejs/src/errors/ConversionError.ts deleted file mode 100644 index 667dd8b7..00000000 --- a/packages/server-nodejs/src/errors/ConversionError.ts +++ /dev/null @@ -1,5 +0,0 @@ - -export default class ConversionError extends Error -{ - -} diff --git a/packages/server-nodejs/src/errors/EnvironmentVariableNotFound.ts b/packages/server-nodejs/src/errors/EnvironmentVariableNotFound.ts deleted file mode 100644 index cc289708..00000000 --- a/packages/server-nodejs/src/errors/EnvironmentVariableNotFound.ts +++ /dev/null @@ -1,8 +0,0 @@ - -export default class EnvironmentVariableNotFound extends Error -{ - constructor(name: string) - { - super(`The environment variable '${name}' is not found`); - } -} diff --git a/packages/server-nodejs/src/errors/RuntimeNotAvailable.ts b/packages/server-nodejs/src/errors/RuntimeNotAvailable.ts deleted file mode 100644 index c90b1ef9..00000000 --- a/packages/server-nodejs/src/errors/RuntimeNotAvailable.ts +++ /dev/null @@ -1,8 +0,0 @@ - -export default class RuntimeNotAvailable extends Error -{ - constructor() - { - super('Runtime is not available'); - } -} diff --git a/packages/server-nodejs/src/errors/UnknownRuntimeMode.ts b/packages/server-nodejs/src/errors/UnknownRuntimeMode.ts deleted file mode 100644 index fd78a284..00000000 --- a/packages/server-nodejs/src/errors/UnknownRuntimeMode.ts +++ /dev/null @@ -1,8 +0,0 @@ - -export default class UnknownRuntimeMode extends Error -{ - constructor() - { - super(`Unknown runtime mode`); - } -} diff --git a/packages/server-nodejs/src/lib.ts b/packages/server-nodejs/src/lib.ts deleted file mode 100644 index d8b0fd5d..00000000 --- a/packages/server-nodejs/src/lib.ts +++ /dev/null @@ -1,4 +0,0 @@ - -export { default as CorsMiddleware } from './middleware/CorsMiddleware.js'; - -export * from './server.js'; diff --git a/packages/server-nodejs/src/models/WorkerDto.ts b/packages/server-nodejs/src/models/WorkerDto.ts deleted file mode 100644 index d578e8ff..00000000 --- a/packages/server-nodejs/src/models/WorkerDto.ts +++ /dev/null @@ -1,25 +0,0 @@ - -import { z } from 'zod'; - -export const workerDtoSchema = z - .object({ - url: z.string().url(), - procedureNames: z.array(z.string()).optional(), - trustKey: z.string().optional() - }) - .strict() - .transform((value) => new WorkerDto(value.url, value.procedureNames, value.trustKey)); - -export default class WorkerDto -{ - url: string; - procedureNames: string[]; - trustKey?: string; - - constructor(url: string, procedureNames: string[] = [], trustKey?: string) - { - this.url = url; - this.procedureNames = procedureNames; - this.trustKey = trustKey; - } -} diff --git a/packages/server-nodejs/src/server.ts b/packages/server-nodejs/src/server.ts deleted file mode 100644 index af67714d..00000000 --- a/packages/server-nodejs/src/server.ts +++ /dev/null @@ -1,15 +0,0 @@ - -import { ModuleImporter, ModuleLoader } from '@jitar/runtime'; - -import JitarServer from './JitarServer.js'; - -export async function buildServer(moduleImporter: ModuleImporter): Promise -{ - ModuleLoader.setImporter(moduleImporter); - - const server = new JitarServer(); - - await server.build(); - - return server; -} diff --git a/packages/server-nodejs/src/utils/DataConverter.ts b/packages/server-nodejs/src/utils/DataConverter.ts deleted file mode 100644 index cfb8da12..00000000 --- a/packages/server-nodejs/src/utils/DataConverter.ts +++ /dev/null @@ -1,21 +0,0 @@ - -import { z } from 'zod'; -import ConversionError from '../errors/ConversionError'; - -export default class DataConverter -{ - static convert(schema: z.ZodSchema, dataObject: object): Type - { - try - { - return schema.parse(dataObject); - } - catch(error: unknown) - { - const errors = (error as z.ZodError).errors; - const message = JSON.stringify(errors); - - throw new ConversionError(message); - } - } -} diff --git a/packages/server-nodejs/src/utils/LogBuilder.ts b/packages/server-nodejs/src/utils/LogBuilder.ts deleted file mode 100644 index 98e1470a..00000000 --- a/packages/server-nodejs/src/utils/LogBuilder.ts +++ /dev/null @@ -1,48 +0,0 @@ - -import { Logger } from 'tslog'; - -export enum LogLevel -{ - DEBUG = 'debug', - INFO = 'info', - WARN = 'warn', - ERROR = 'error', - FATAL = 'fatal' -} - -export default class LogBuilder -{ - static build(level: string): Logger - { - const logConfiguration = this.#getLogConfiguration(level); - - return new Logger(logConfiguration); - } - - static #getLogConfiguration(level: string): object - { - const logLevel = this.#getLogLevel(level); - - return { - prettyLogTemplate: "{{dateIsoStr}}\t{{logLevelName}}\t", - minLevel: logLevel - }; - } - - static #getLogLevel(level: string): number - { - switch (level) - { - case LogLevel.FATAL: - return 6; - case LogLevel.ERROR: - return 5; - case LogLevel.WARN: - return 4; - case LogLevel.INFO: - return 3; - default: - return 2; - } - } -} diff --git a/packages/server-nodejs/src/utils/RuntimeConfigurationLoader.ts b/packages/server-nodejs/src/utils/RuntimeConfigurationLoader.ts deleted file mode 100644 index bc29694f..00000000 --- a/packages/server-nodejs/src/utils/RuntimeConfigurationLoader.ts +++ /dev/null @@ -1,37 +0,0 @@ - -import { readFileSync } from 'fs'; - -import RuntimeConfiguration, { runtimeSchema } from '../configuration/RuntimeConfiguration.js'; - -import EnvironmentVariableNotFound from '../errors/EnvironmentVariableNotFound.js'; - -import DataConverter from './DataConverter.js'; - -const ENVIRONMENT_VARIABLE_REGEX = /\${([^}]*)}/g; - -export default class RuntimeConfigurationLoader -{ - static load(filename: string): RuntimeConfiguration - { - const plainContents = readFileSync(filename, 'utf-8'); - const replacedContents = this.#replaceEnvironmentVariables(plainContents); - const parsedContents = JSON.parse(replacedContents); - - return DataConverter.convert(runtimeSchema, parsedContents); - } - - static #replaceEnvironmentVariables(contents: string): string - { - return contents.replace(ENVIRONMENT_VARIABLE_REGEX, (match, group) => - { - const value = process.env[group]; - - if (value === undefined) - { - throw new EnvironmentVariableNotFound(group); - } - - return value; - }); - } -} diff --git a/packages/server-nodejs/src/utils/RuntimeConfigurator.ts b/packages/server-nodejs/src/utils/RuntimeConfigurator.ts deleted file mode 100644 index f8f2fdc2..00000000 --- a/packages/server-nodejs/src/utils/RuntimeConfigurator.ts +++ /dev/null @@ -1,174 +0,0 @@ - -import { Runtime, RuntimeBuilder, LocalRepository, LocalGateway, LocalWorker, WorkerMonitor, Proxy, Standalone } from '@jitar/runtime'; -import { CacheManager } from '@jitar/caching'; - -import RuntimeConfiguration from '../configuration/RuntimeConfiguration.js'; -import StandaloneConfiguration from '../configuration/StandaloneConfiguration.js'; -import RepositoryConfiguration from '../configuration/RepositoryConfiguration.js'; -import GatewayConfiguration from '../configuration/GatewayConfiguration.js'; -import WorkerConfiguration from '../configuration/WorkerConfiguration.js'; -import ProxyConfiguration from '../configuration/ProxyConfiguration.js'; - -import RuntimeDefaults from '../definitions/RuntimeDefaults.js'; - -import UnknownRuntimeMode from '../errors/UnknownRuntimeMode.js'; - -import LocalFileManager from './LocalFileManager.js'; - -export default class RuntimeConfigurator -{ - static async configure(configuration: RuntimeConfiguration): Promise - { - const url = configuration.url ?? RuntimeDefaults.URL; - const healthChecks = configuration.healthChecks ?? []; - - if (configuration.standalone !== undefined) return this.#configureStandAlone(url, healthChecks, configuration.standalone); - if (configuration.repository !== undefined) return this.#configureRepository(url, healthChecks, configuration.repository); - if (configuration.gateway !== undefined) return this.#configureGateway(url, healthChecks, configuration.gateway); - if (configuration.worker !== undefined) return this.#configureWorker(url, healthChecks, configuration.worker); - if (configuration.proxy !== undefined) return this.#configureProxy(url, healthChecks, configuration.proxy); - - throw new UnknownRuntimeMode(); - } - - static async #configureStandAlone(url: string, healthChecks: string[], configuration: StandaloneConfiguration): Promise - { - const sourceLocation = configuration.source ?? RuntimeDefaults.SOURCE; - const cacheLocation = configuration.cache ?? RuntimeDefaults.CACHE; - const overrides = configuration.overrides ?? {}; - const middlewares = configuration.middlewares ?? []; - const fileManager = new LocalFileManager(cacheLocation); - const trustKey = configuration.trustKey; - - await this.#buildCache(sourceLocation, cacheLocation); - - const segmentNames = configuration.segments ?? await this.#getWorkerSegmentNames(fileManager); - - const assets = configuration.assets !== undefined - ? await fileManager.getAssetFiles(configuration.assets) - : []; - - return new RuntimeBuilder() - .url(url) - .healthCheck(...healthChecks) - .middleware(...middlewares) - .segment(...segmentNames) - .asset(...assets) - .override(overrides) - .fileManager(fileManager) - .buildStandalone(trustKey); - } - - static async #configureRepository(url: string, healthChecks: string[], configuration: RepositoryConfiguration): Promise - { - const sourceLocation = configuration.source ?? RuntimeDefaults.SOURCE; - const cacheLocation = configuration.cache ?? RuntimeDefaults.CACHE; - const overrides = configuration.overrides ?? {}; - const fileManager = new LocalFileManager(cacheLocation); - - await this.#buildCache(sourceLocation, cacheLocation); - - const segmentNames = await this.#getRepositorySegmentNames(fileManager); - - const assets = configuration.assets !== undefined - ? await fileManager.getAssetFiles(configuration.assets) - : []; - - return new RuntimeBuilder() - .url(url) - .healthCheck(...healthChecks) - .segment(...segmentNames) - .asset(...assets) - .override(overrides) - .fileManager(fileManager) - .buildRepository(); - } - - static async #configureGateway(url: string, healthChecks: string[], configuration: GatewayConfiguration): Promise - { - const repositoryUrl = configuration.repository; - const middlewares = configuration.middlewares ?? []; - const monitorInterval = configuration.monitor; - const trustKey = configuration.trustKey; - - const gateway = new RuntimeBuilder() - .url(url) - .healthCheck(...healthChecks) - .middleware(...middlewares) - .repository(repositoryUrl) - .buildGateway(trustKey); - - new WorkerMonitor(gateway, monitorInterval); - - return gateway; - } - - static async #configureWorker(url: string, healthChecks: string[], configuration: WorkerConfiguration): Promise - { - const repositoryUrl = configuration.repository; - const gatewayUrl = configuration.gateway; - const segmentNames = configuration.segments ?? []; - const middlewares = configuration.middlewares ?? []; - const trustKey = configuration.trustKey; - - return new RuntimeBuilder() - .url(url) - .healthCheck(...healthChecks) - .middleware(...middlewares) - .repository(repositoryUrl) - .gateway(gatewayUrl) - .segment(...segmentNames) - .buildWorker(trustKey); - } - - static async #configureProxy(url: string, healthChecks: string[], configuration: ProxyConfiguration): Promise - { - const repositoryUrl = configuration.repository; - const gatewayUrl = configuration.gateway; - const workerUrl = configuration.worker; - const middlewares = configuration.middlewares ?? []; - - return new RuntimeBuilder() - .url(url) - .healthCheck(...healthChecks) - .middleware(...middlewares) - .repository(repositoryUrl) - .gateway(gatewayUrl) - .worker(workerUrl) - .buildProxy(); - } - - static async #buildCache(sourceLocation: string, cacheLocation: string): Promise - { - const projectFileManager = new LocalFileManager('./'); - await projectFileManager.delete(cacheLocation); - await projectFileManager.copy(sourceLocation, cacheLocation); - - const appFileManager = new LocalFileManager(cacheLocation); - - const cacheManager = new CacheManager(projectFileManager, appFileManager); - await cacheManager.build(); - } - - static async #getWorkerSegmentNames(fileManager: LocalFileManager): Promise - { - const segmentFilenames = await fileManager.getWorkerSegmentFiles(); - - return segmentFilenames.map(filename => this.#extractSegmentName(filename)); - } - - static async #getRepositorySegmentNames(fileManager: LocalFileManager): Promise - { - const segmentFilenames = await fileManager.getRepositorySegmentFiles(); - - return segmentFilenames.map(filename => this.#extractSegmentName(filename)); - } - - static #extractSegmentName(filename: string): string - { - const name = filename.split('/').pop() ?? ''; - const endIndex = name.indexOf('.segment'); - - return name.substring(0, endIndex); - } -} diff --git a/packages/server-nodejs/src/utils/ServerOptionsReader.ts b/packages/server-nodejs/src/utils/ServerOptionsReader.ts deleted file mode 100644 index 3376b88e..00000000 --- a/packages/server-nodejs/src/utils/ServerOptionsReader.ts +++ /dev/null @@ -1,15 +0,0 @@ - -import yargs from 'yargs'; - -import ServerOptions, { serverOptionsSchema } from '../configuration/ServerOptions.js'; - -import DataConverter from './DataConverter.js'; - -export default class ServerOptionsReader -{ - static read(): ServerOptions - { - const args: object = yargs(process.argv).argv; - return DataConverter.convert(serverOptionsSchema, args); - } -} diff --git a/packages/services/CHANGELOG.md b/packages/services/CHANGELOG.md new file mode 100644 index 00000000..80f29432 --- /dev/null +++ b/packages/services/CHANGELOG.md @@ -0,0 +1,4 @@ + +# Changelog + +This package doesn't keep a changelog. See the changelog in the [github repository](https://github.com/MaskingTechnology/jitar/blob/main/CHANGELOG.md) \ No newline at end of file diff --git a/packages/services/README.md b/packages/services/README.md new file mode 100644 index 00000000..fcf688ee --- /dev/null +++ b/packages/services/README.md @@ -0,0 +1,9 @@ + +# Jitar Health + +This package provides the services of the [Jitar](https://jitar.dev) runtime. + +For more information about Jitar: + +* [Visit our website](https://jitar.dev) +* [Read the documentation](https://docs.jitar.dev). diff --git a/packages/services/package.json b/packages/services/package.json new file mode 100644 index 00000000..7e011f78 --- /dev/null +++ b/packages/services/package.json @@ -0,0 +1,24 @@ +{ + "name": "@jitar/services", + "version": "0.7.4", + "description": "Services library for the Jitar runtime.", + "author": "Masking Technology (https://jitar.dev)", + "license": "MIT", + "type": "module", + "types": "dist/index.d.ts", + "exports": "./dist/index.js", + "private": true, + "scripts": { + "test": "vitest run", + "test-coverage": "vitest run --coverage", + "lint": "eslint . --ext .ts", + "build": "tsc -p tsconfig.json", + "clean": "rm -rf dist" + }, + "dependencies": { + "@jitar/errors": "*", + "@jitar/execution": "*", + "@jitar/serialization": "*", + "@jitar/sourcing": "*" + } +} diff --git a/packages/services/src/ProviderService.ts b/packages/services/src/ProviderService.ts new file mode 100644 index 00000000..b3ca813b --- /dev/null +++ b/packages/services/src/ProviderService.ts @@ -0,0 +1,11 @@ + +import type { File } from '@jitar/sourcing'; + +import Service from './Service'; + +interface ProviderService extends Service +{ + provide(filename: string): Promise; +} + +export default ProviderService; diff --git a/packages/services/src/Remote.ts b/packages/services/src/Remote.ts new file mode 100644 index 00000000..da32578c --- /dev/null +++ b/packages/services/src/Remote.ts @@ -0,0 +1,22 @@ + +import { Request, Response as ResultResponse } from '@jitar/execution'; +import { File } from '@jitar/sourcing'; + +interface Remote +{ + connect(): Promise; + + disconnect(): Promise; + + provide(filename: string): Promise; + + isHealthy(): Promise; + + getHealth(): Promise>; + + addWorker(workerUrl: string, procedureNames: string[], trustKey?: string): Promise + + run(request: Request): Promise; +} + +export default Remote; diff --git a/packages/services/src/RemoteBuilder.ts b/packages/services/src/RemoteBuilder.ts new file mode 100644 index 00000000..225aefe8 --- /dev/null +++ b/packages/services/src/RemoteBuilder.ts @@ -0,0 +1,9 @@ + +import Remote from './Remote'; + +interface RemoteBuilder +{ + build(url: string): Remote; +} + +export default RemoteBuilder; diff --git a/packages/services/src/RunnerService.ts b/packages/services/src/RunnerService.ts new file mode 100644 index 00000000..fcbb31e0 --- /dev/null +++ b/packages/services/src/RunnerService.ts @@ -0,0 +1,15 @@ + +import { Runner } from '@jitar/execution'; + +import Service from './Service'; + +interface RunnerService extends Runner, Service +{ + get trustKey(): string | undefined; + + getProcedureNames(): string[]; + + hasProcedure(name: string): boolean; +} + +export default RunnerService; diff --git a/packages/services/src/Service.ts b/packages/services/src/Service.ts new file mode 100644 index 00000000..4608256a --- /dev/null +++ b/packages/services/src/Service.ts @@ -0,0 +1,15 @@ + +interface Service +{ + get url(): string; + + start(): Promise; + + stop(): Promise; + + isHealthy(): Promise; + + getHealth(): Promise> +} + +export default Service; diff --git a/packages/services/src/dummy/DummyProvider.ts b/packages/services/src/dummy/DummyProvider.ts new file mode 100644 index 00000000..3ce1ac0b --- /dev/null +++ b/packages/services/src/dummy/DummyProvider.ts @@ -0,0 +1,39 @@ + +import { NotImplemented } from '@jitar/errors'; +import { File } from '@jitar/sourcing'; + +import ProviderService from '../ProviderService'; + +export default class DummyProvider implements ProviderService +{ + get url(): string + { + throw new NotImplemented(); + } + + start(): Promise + { + return Promise.resolve(); + } + + stop(): Promise + { + return Promise.resolve(); + } + + async isHealthy(): Promise + { + return true; + } + + async getHealth(): Promise> + { + return new Map(); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + provide(filename: string): Promise + { + throw new NotImplemented(); + } +} diff --git a/packages/services/src/dummy/DummyRunner.ts b/packages/services/src/dummy/DummyRunner.ts new file mode 100644 index 00000000..67ebeb8e --- /dev/null +++ b/packages/services/src/dummy/DummyRunner.ts @@ -0,0 +1,55 @@ + +import { NotImplemented } from '@jitar/errors'; +import { Request, Response } from '@jitar/execution'; + +import RunnerService from '../RunnerService'; + +export default class DummyRunner implements RunnerService +{ + get url(): string + { + throw new NotImplemented(); + } + + get trustKey(): string | undefined + { + throw new NotImplemented(); + } + + start(): Promise + { + return Promise.resolve(); + } + + stop(): Promise + { + return Promise.resolve(); + } + + async isHealthy(): Promise + { + return true; + } + + async getHealth(): Promise> + { + return new Map(); + } + + getProcedureNames(): string[] + { + throw new NotImplemented(); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + hasProcedure(name: string): boolean + { + throw new NotImplemented(); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + run(request: Request): Promise + { + throw new NotImplemented(); + } +} diff --git a/packages/services/src/gateway/Gateway.ts b/packages/services/src/gateway/Gateway.ts new file mode 100644 index 00000000..928fb7b0 --- /dev/null +++ b/packages/services/src/gateway/Gateway.ts @@ -0,0 +1,10 @@ + +import RunnerService from '../RunnerService'; +import Worker from '../worker/Worker'; + +interface Gateway extends RunnerService +{ + addWorker(worker: Worker): Promise; +} + +export default Gateway; diff --git a/packages/services/src/gateway/LocalGateway.ts b/packages/services/src/gateway/LocalGateway.ts new file mode 100644 index 00000000..ec510bbe --- /dev/null +++ b/packages/services/src/gateway/LocalGateway.ts @@ -0,0 +1,88 @@ + +import { Response } from '@jitar/execution'; +import type { Request } from '@jitar/execution'; + +import Worker from '../worker/Worker'; + +import Gateway from './Gateway'; +import WorkerManager from './WorkerManager'; +import WorkerMonitor from './WorkerMonitor'; + +import InvalidTrustKey from './errors/InvalidTrustKey'; + +type Configuration = +{ + url: string; + trustKey?: string; + monitorInterval?: number; +}; + +export default class LocalGateway implements Gateway +{ + #url: string; + #trustKey?: string; + #workerManager: WorkerManager; + #workerMonitor: WorkerMonitor; + + constructor(configuration: Configuration) + { + this.#url = configuration.url; + this.#trustKey = configuration.trustKey; + this.#workerManager = new WorkerManager(); + this.#workerMonitor = new WorkerMonitor(this.#workerManager, configuration.monitorInterval); + } + + get url() { return this.#url; } + + get trustKey() { return this.#trustKey; } + + async start(): Promise + { + return this.#workerMonitor.start(); + } + + async stop(): Promise + { + return this.#workerMonitor.stop(); + } + + async isHealthy(): Promise + { + return true; + } + + async getHealth(): Promise> + { + return new Map(); + } + + async addWorker(worker: Worker, trustKey?: string): Promise + { + if (this.#isInvalidTrustKey(trustKey)) + { + throw new InvalidTrustKey(); + } + + return this.#workerManager.addWorker(worker); + } + + getProcedureNames(): string[] + { + return this.#workerManager.getProcedureNames(); + } + + hasProcedure(name: string): boolean + { + return this.#workerManager.hasProcedure(name); + } + + async run(request: Request): Promise + { + return this.#workerManager.run(request); + } + + #isInvalidTrustKey(trustKey?: string): boolean + { + return this.#trustKey !== undefined && trustKey !== this.#trustKey; + } +} diff --git a/packages/services/src/gateway/RemoteGateway.ts b/packages/services/src/gateway/RemoteGateway.ts new file mode 100644 index 00000000..66f5180a --- /dev/null +++ b/packages/services/src/gateway/RemoteGateway.ts @@ -0,0 +1,71 @@ + +import { NotImplemented } from '@jitar/errors'; +import { Request, Response } from '@jitar/execution'; + +import Remote from '../Remote'; +import Worker from '../worker/Worker'; + +import Gateway from './Gateway'; + +type Configuration = +{ + url: string; + remote: Remote; +}; + +export default class RemoteGateway implements Gateway +{ + #url: string; + #remote: Remote; + + constructor(configuration: Configuration) + { + this.#url = configuration.url; + this.#remote = configuration.remote; + } + + get url() { return this.#url; } + + get trustKey() { return undefined; } + + start(): Promise + { + return this.#remote.connect(); + } + + stop(): Promise + { + return this.#remote.disconnect(); + } + + isHealthy(): Promise + { + return this.#remote.isHealthy(); + } + + getHealth(): Promise> + { + return this.#remote.getHealth(); + } + + getProcedureNames(): string[] + { + throw new NotImplemented(); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + hasProcedure(name: string): boolean + { + throw new NotImplemented(); + } + + addWorker(worker: Worker): Promise + { + return this.#remote.addWorker(worker.url, worker.getProcedureNames(), worker.trustKey); + } + + run(request: Request): Promise + { + return this.#remote.run(request); + } +} diff --git a/packages/runtime/src/services/WorkerBalancer.ts b/packages/services/src/gateway/WorkerBalancer.ts similarity index 79% rename from packages/runtime/src/services/WorkerBalancer.ts rename to packages/services/src/gateway/WorkerBalancer.ts index dedafa51..57fce3db 100644 --- a/packages/runtime/src/services/WorkerBalancer.ts +++ b/packages/services/src/gateway/WorkerBalancer.ts @@ -1,16 +1,17 @@ -import NoWorkerAvailable from '../errors/NoWorkerAvailable.js'; +import type { Request, Response } from '@jitar/execution'; -import Request from '../models/Request.js'; -import Response from '../models/Response.js'; +import type Worker from '../worker/Worker'; -import Worker from './Worker.js'; +import NoWorkerAvailable from './errors/NoWorkerAvailable'; export default class WorkerBalancer { #workers: Worker[] = []; #currentIndex = 0; + get workers() { return this.#workers; } + addWorker(worker: Worker): void { if (this.#workers.includes(worker)) @@ -48,7 +49,7 @@ export default class WorkerBalancer return this.#workers[this.#currentIndex++]; } - run(request: Request): Promise + async run(request: Request): Promise { const worker = this.getNextWorker(); diff --git a/packages/runtime/src/services/LocalGateway.ts b/packages/services/src/gateway/WorkerManager.ts similarity index 67% rename from packages/runtime/src/services/LocalGateway.ts rename to packages/services/src/gateway/WorkerManager.ts index 0802db28..273c82a9 100644 --- a/packages/runtime/src/services/LocalGateway.ts +++ b/packages/services/src/gateway/WorkerManager.ts @@ -1,33 +1,21 @@ -import InvalidTrustKey from '../errors/InvalidTrustKey.js'; -import ProcedureNotFound from '../errors/ProcedureNotFound.js'; +import { Request, Response, Runner, ProcedureNotFound } from '@jitar/execution'; -import Request from '../models/Request.js'; -import Response from '../models/Response.js'; +import Worker from '../worker/Worker'; +import WorkerBalancer from './WorkerBalancer'; -import Gateway from './Gateway.js'; -import Worker from './Worker.js'; -import WorkerBalancer from './WorkerBalancer.js'; -import Repository from './Repository.js'; - -export default class LocalGateway extends Gateway +export default class WorkerManager implements Runner { #workers: Set = new Set(); #balancers: Map = new Map(); - #trustKey?: string; - - constructor(repository: Repository, url?: string, trustKey?: string) - { - super(repository, url); - - this.#trustKey = trustKey; - } get workers() { return [...this.#workers.values()]; } + get balancers() { return this.#balancers; } + getProcedureNames(): string[] { const procedureNames = this.workers.map(worker => worker.getProcedureNames()); @@ -43,13 +31,8 @@ export default class LocalGateway extends Gateway return procedureNames.includes(fqn); } - async addWorker(worker: Worker, trustKey?: string): Promise + async addWorker(worker: Worker): Promise { - if (trustKey !== undefined && this.#trustKey !== trustKey) - { - throw new InvalidTrustKey(); - } - this.#workers.add(worker); for (const name of worker.getProcedureNames()) @@ -96,7 +79,7 @@ export default class LocalGateway extends Gateway return balancer; } - run(request: Request): Promise + async run(request: Request): Promise { const balancer = this.#getBalancer(request.fqn); diff --git a/packages/runtime/src/services/WorkerMonitor.ts b/packages/services/src/gateway/WorkerMonitor.ts similarity index 64% rename from packages/runtime/src/services/WorkerMonitor.ts rename to packages/services/src/gateway/WorkerMonitor.ts index f1ed8702..ed5a1891 100644 --- a/packages/runtime/src/services/WorkerMonitor.ts +++ b/packages/services/src/gateway/WorkerMonitor.ts @@ -1,21 +1,24 @@ -import LocalGateway from './LocalGateway.js'; -import Worker from './Worker.js'; +import type Worker from '../worker/Worker'; + +import type WorkerManager from './WorkerManager'; const DEFAULT_FREQUENCY = 5000; export default class WorkerMonitor { - #gateway: LocalGateway; + #workerManager: WorkerManager; #frequency: number; #interval: ReturnType | null = null; - constructor(gateway: LocalGateway, frequency: number = DEFAULT_FREQUENCY) + constructor(workerManager: WorkerManager, frequency = DEFAULT_FREQUENCY) { - this.#gateway = gateway; + this.#workerManager = workerManager; this.#frequency = frequency; } + get workerManager() { return this.#workerManager; } + start(): void { this.#interval = setInterval(async () => this.#monitor(), this.#frequency); @@ -33,8 +36,8 @@ export default class WorkerMonitor async #monitor(): Promise { - const workers = this.#gateway.workers; - const promises = workers.map(async (worker: Worker) => this.#monitorWorker(worker)); + const workers = this.#workerManager.workers; + const promises = workers.map(worker => this.#monitorWorker(worker)); await Promise.all(promises); } @@ -45,7 +48,7 @@ export default class WorkerMonitor if (available === false) { - this.#gateway.removeWorker(worker); + this.#workerManager.removeWorker(worker); } } @@ -55,7 +58,7 @@ export default class WorkerMonitor { return await worker.isHealthy(); } - catch (error) + catch (error: unknown) { return false; } diff --git a/packages/services/src/gateway/errors/InvalidTrustKey.ts b/packages/services/src/gateway/errors/InvalidTrustKey.ts new file mode 100644 index 00000000..4042d9a9 --- /dev/null +++ b/packages/services/src/gateway/errors/InvalidTrustKey.ts @@ -0,0 +1,10 @@ + +import { Unauthorized } from '@jitar/errors'; + +export default class InvalidTrustKey extends Unauthorized +{ + constructor() + { + super(`Invalid trust key`); + } +} diff --git a/packages/runtime/src/errors/NoWorkerAvailable.ts b/packages/services/src/gateway/errors/NoWorkerAvailable.ts similarity index 60% rename from packages/runtime/src/errors/NoWorkerAvailable.ts rename to packages/services/src/gateway/errors/NoWorkerAvailable.ts index c8bc4d64..b145060e 100644 --- a/packages/runtime/src/errors/NoWorkerAvailable.ts +++ b/packages/services/src/gateway/errors/NoWorkerAvailable.ts @@ -1,7 +1,5 @@ -import { Loadable } from '@jitar/serialization'; - -import ServerError from './generic/ServerError.js'; +import { ServerError } from '@jitar/errors'; export default class NoWorkerAvailable extends ServerError { @@ -16,5 +14,3 @@ export default class NoWorkerAvailable extends ServerError get name() { return this.#name; } } - -(NoWorkerAvailable as Loadable).source = 'RUNTIME_ERROR_LOCATION'; diff --git a/packages/services/src/index.ts b/packages/services/src/index.ts new file mode 100644 index 00000000..25b91eb7 --- /dev/null +++ b/packages/services/src/index.ts @@ -0,0 +1,23 @@ + +export { default as DummyProvider } from './dummy/DummyProvider'; +export { default as DummyRunner } from './dummy/DummyRunner'; + +export { default as Gateway } from './gateway/Gateway'; +export { default as LocalGateway } from './gateway/LocalGateway'; +export { default as RemoteGateway } from './gateway/RemoteGateway'; + +export { default as Proxy } from './proxy/Proxy'; + +export { default as Repository } from './repository/Repository'; +export { default as LocalRepository } from './repository/LocalRepository'; +export { default as RemoteRepository } from './repository/RemoteRepository'; + +export { default as Worker } from './worker/Worker'; +export { default as LocalWorker } from './worker/LocalWorker'; +export { default as RemoteWorker } from './worker/RemoteWorker'; + +export { default as Service } from './Service'; +export { default as RunnerService } from './RunnerService'; +export { default as ProviderService } from './ProviderService'; +export { default as Remote } from './Remote'; +export { default as RemoteBuilder } from './RemoteBuilder'; diff --git a/packages/services/src/proxy/Proxy.ts b/packages/services/src/proxy/Proxy.ts new file mode 100644 index 00000000..6cc3f0e6 --- /dev/null +++ b/packages/services/src/proxy/Proxy.ts @@ -0,0 +1,91 @@ + +import type { Request, Response } from '@jitar/execution'; +import type { File } from '@jitar/sourcing'; + +import RunnerService from '../RunnerService'; +import ProviderService from '../ProviderService'; + +type Configuration = +{ + url: string; + provider: ProviderService; + runner: RunnerService; +}; + +export default class Proxy implements ProviderService, RunnerService +{ + #url: string; + #provider: ProviderService; + #runner: RunnerService; + + constructor(configuration: Configuration) + { + this.#url = configuration.url; + this.#provider = configuration.provider; + this.#runner = configuration.runner; + } + + get url() { return this.#url; } + + get trustKey() { return this.#runner.trustKey; } + + get provider() { return this.#provider; } + + get runner() { return this.#runner; } + + async isHealthy(): Promise + { + const [providerHealthy, runnerHealthy] = await Promise.all([ + this.#provider.isHealthy(), + this.#runner.isHealthy() + ]); + + return providerHealthy && runnerHealthy; + } + + async getHealth(): Promise> + { + const [providerHealth, runnerHealth] = await Promise.all([ + this.#provider.getHealth(), + this.#runner.getHealth() + ]); + + return new Map([...providerHealth, ...runnerHealth]); + } + + async start(): Promise + { + await Promise.all([ + this.#provider.start(), + this.#runner.start() + ]); + } + + async stop(): Promise + { + await Promise.all([ + this.#runner.stop(), + this.#provider.stop() + ]); + } + + provide(filename: string): Promise + { + return this.#provider.provide(filename); + } + + getProcedureNames(): string[] + { + return this.#runner.getProcedureNames(); + } + + hasProcedure(fqn: string): boolean + { + return this.#runner.hasProcedure(fqn); + } + + run(request: Request): Promise + { + return this.#runner.run(request); + } +} diff --git a/packages/services/src/repository/LocalRepository.ts b/packages/services/src/repository/LocalRepository.ts new file mode 100644 index 00000000..977de25a --- /dev/null +++ b/packages/services/src/repository/LocalRepository.ts @@ -0,0 +1,89 @@ + +import { File, FileNotFound, SourcingManager } from '@jitar/sourcing'; + +import Repository from './Repository.js'; + +type Configuration = +{ + url: string; + assets: Set; + sourcingManager: SourcingManager; + indexFilename?: string; + serveIndexOnNotFound?: boolean; +}; + +const DEFAULT_INDEX_FILENAME = 'index.html'; +const DEFAULT_SERVE_INDEX_ON_NOT_FOUND = false; + +export default class LocalRepository implements Repository +{ + #url: string; + #sourcingManager: SourcingManager; + #assets: Set; + + #indexFilename: string; + #serveIndexOnNotFound: boolean; + + constructor(configuration: Configuration) + { + this.#url = configuration.url; + this.#sourcingManager = configuration.sourcingManager; + this.#assets = configuration.assets; + + this.#indexFilename = configuration.indexFilename ?? DEFAULT_INDEX_FILENAME; + this.#serveIndexOnNotFound = configuration.serveIndexOnNotFound ?? DEFAULT_SERVE_INDEX_ON_NOT_FOUND; + } + + get url() { return this.#url; } + + start(): Promise + { + return Promise.resolve(); + } + + stop(): Promise + { + return Promise.resolve(); + } + + async isHealthy(): Promise + { + return true; + } + + async getHealth(): Promise> + { + return new Map(); + } + + async provide(filename: string): Promise + { + if (this.#mustProvideIndex(filename)) + { + return this.provide(this.#indexFilename); + } + + if (this.#assets.has(filename) === false) + { + throw new FileNotFound(filename); + } + + return this.#sourcingManager.read(filename); + } + + #mustProvideIndex(filename: string): boolean + { + if (filename === '') + { + return true; + } + + if (filename === this.#indexFilename) + { + return false; + } + + return this.#serveIndexOnNotFound + && this.#assets.has(filename) === false; + } +} diff --git a/packages/services/src/repository/RemoteRepository.ts b/packages/services/src/repository/RemoteRepository.ts new file mode 100644 index 00000000..cbdc1867 --- /dev/null +++ b/packages/services/src/repository/RemoteRepository.ts @@ -0,0 +1,51 @@ + +import { File } from '@jitar/sourcing'; + +import Remote from '../Remote'; + +import Repository from './Repository'; + +type Configuration = +{ + url: string; + remote: Remote; +}; + +export default class RemoteRepository implements Repository +{ + #url: string; + #remote: Remote; + + constructor(configuration: Configuration) + { + this.#url = configuration.url; + this.#remote = configuration.remote; + } + + get url() { return this.#url; } + + start(): Promise + { + return this.#remote.connect(); + } + + stop(): Promise + { + return this.#remote.disconnect(); + } + + isHealthy(): Promise + { + return this.#remote.isHealthy(); + } + + getHealth(): Promise> + { + return this.#remote.getHealth(); + } + + provide(filename: string): Promise + { + return this.#remote.provide(filename); + } +} diff --git a/packages/services/src/repository/Repository.ts b/packages/services/src/repository/Repository.ts new file mode 100644 index 00000000..564c4f37 --- /dev/null +++ b/packages/services/src/repository/Repository.ts @@ -0,0 +1,9 @@ + +import ProviderService from '../ProviderService'; + +interface Repository extends ProviderService +{ + // No additional methods +} + +export default Repository; diff --git a/packages/services/src/worker/ExecutionClassResolver.ts b/packages/services/src/worker/ExecutionClassResolver.ts new file mode 100644 index 00000000..121ac6cd --- /dev/null +++ b/packages/services/src/worker/ExecutionClassResolver.ts @@ -0,0 +1,27 @@ + +import { ClassResolver } from '@jitar/serialization'; +import type { ExecutionManager } from '@jitar/execution'; + +export default class ExecutionClassResolver implements ClassResolver +{ + #executionManager: ExecutionManager; + + constructor(executionManager: ExecutionManager) + { + this.#executionManager = executionManager; + } + + resolveKey(clazz: Function): string | undefined + { + const model = this.#executionManager.getClassByImplementation(clazz); + + return model?.fqn; + } + + resolveClass(key: string): Function | undefined + { + const model = this.#executionManager.getClass(key); + + return model?.implementation; + } +} diff --git a/packages/services/src/worker/LocalWorker.ts b/packages/services/src/worker/LocalWorker.ts new file mode 100644 index 00000000..43a59178 --- /dev/null +++ b/packages/services/src/worker/LocalWorker.ts @@ -0,0 +1,205 @@ + +import { ExecutionManager, Request, Response, Implementation, ProcedureNotFound } from '@jitar/execution'; +import { Serializer, SerializerBuilder } from '@jitar/serialization'; + +import Gateway from '../gateway/Gateway'; +import Worker from './Worker'; + +import ExecutionClassResolver from './ExecutionClassResolver'; +import RequestNotTrusted from './errors/RequestNotTrusted'; + +const JITAR_TRUST_HEADER_KEY = 'X-Jitar-Trust-Key'; +const JITAR_DATA_ENCODING_KEY = 'X-Jitar-Data-Encoding'; +const JITAR_DATA_ENCODING_VALUE = 'serialized'; + +type Configuration = +{ + url: string; + trustKey?: string; + gateway?: Gateway; + executionManager: ExecutionManager; +}; + +export default class LocalWorker implements Worker +{ + #url: string; + #trustKey?: string; + #gateway?: Gateway; + #executionManager: ExecutionManager; + #serializer: Serializer; + + constructor(configuration: Configuration) + { + this.#url = configuration.url; + this.#trustKey = configuration.trustKey; + this.#gateway = configuration.gateway; + + this.#executionManager = configuration.executionManager; + + const classResolver = new ExecutionClassResolver(this.#executionManager); + this.#serializer = SerializerBuilder.build(classResolver); + } + + get url() { return this.#url; } + + get trustKey() { return this.#trustKey; } + + async start(): Promise + { + if (this.#gateway !== undefined) + { + await this.#gateway.start(); + await this.#gateway.addWorker(this); + } + } + + async stop(): Promise + { + if (this.#gateway !== undefined) + { + // TODO: Remove worker from gateway (Github issue #410) + await this.#gateway.stop(); + } + } + + getProcedureNames(): string[] + { + return this.#executionManager.getProcedureNames(); + } + + hasProcedure(name: string): boolean + { + return this.#executionManager.hasProcedure(name); + } + + async isHealthy(): Promise + { + return true; + } + + async getHealth(): Promise> + { + return new Map(); + } + + async run(request: Request): Promise + { + return this.#mustRunLocal(request) + ? this.#runLocal(request) + : this.#runRemote(request); + } + + #mustRunLocal(request: Request): boolean + { + return this.#gateway === undefined + || this.#executionManager.hasProcedure(request.fqn); + } + + async #runLocal(request: Request): Promise + { + const procedure = this.#executionManager.getProcedure(request.fqn); + const implementation = procedure?.getImplementation(request.version); + + if (this.#procedureNotFound(implementation)) + { + throw new ProcedureNotFound(request.fqn); + } + + if (this.#requestNotTrusted(request, implementation!)) + { + throw new RequestNotTrusted(); + } + + const dataEncoding = request.getHeader(JITAR_DATA_ENCODING_KEY); + + if (dataEncoding === JITAR_DATA_ENCODING_VALUE) + { + request = await this.#deserializeRequest(request); + } + + request.removeHeader(JITAR_DATA_ENCODING_KEY); + + const response = await this.#executionManager.run(request); + + if (dataEncoding === JITAR_DATA_ENCODING_VALUE) + { + return this.#serializeResponse(response); + } + + return response; + } + + #procedureNotFound(implementation?: Implementation): boolean + { + return implementation === undefined + || implementation.private; + } + + #requestNotTrusted(request: Request, implementation: Implementation): boolean + { + // Public requests are always allowed + if (implementation.public) return false; + + const trustKey = request.getHeader(JITAR_TRUST_HEADER_KEY); + + return this.#trustKey !== trustKey; + } + + async #runRemote(request: Request): Promise + { + request = await this.#serializeRequest(request); + + request.setHeader(JITAR_DATA_ENCODING_KEY, JITAR_DATA_ENCODING_VALUE); + + if (this.#trustKey !== undefined) + { + request.setHeader(JITAR_TRUST_HEADER_KEY, this.#trustKey); + } + + const response = await this.#gateway!.run(request); + + return this.#deserializeResponse(response); + } + + async #serializeRequest(request: Request): Promise + { + const serializedArgs: Map = new Map(); + + for (const [key, value] of request.args) + { + const serializedValue = await this.#serializer.serialize(value); + + serializedArgs.set(key, serializedValue); + } + + return new Request(request.fqn, request.version, serializedArgs, request.headers, request.mode); + } + + async #deserializeRequest(request: Request): Promise + { + const deserializedArgs: Map = new Map(); + + for (const [key, value] of request.args) + { + const deserializedValue = await this.#serializer.deserialize(value); + + deserializedArgs.set(key, deserializedValue); + } + + return new Request(request.fqn, request.version, deserializedArgs, request.headers, request.mode); + } + + async #serializeResponse(response: Response): Promise + { + const serializedResult = await this.#serializer.serialize(response.result); + + return new Response(response.status, serializedResult, response.headers); + } + + async #deserializeResponse(response: Response): Promise + { + const deserializedResult = await this.#serializer.deserialize(response.result); + + return new Response(response.status, deserializedResult, response.headers); + } +} diff --git a/packages/services/src/worker/RemoteWorker.ts b/packages/services/src/worker/RemoteWorker.ts new file mode 100644 index 00000000..67643c52 --- /dev/null +++ b/packages/services/src/worker/RemoteWorker.ts @@ -0,0 +1,66 @@ + +import { Request, Response } from '@jitar/execution'; + +import Remote from '../Remote'; + +import Worker from './Worker'; + +type Configuration = +{ + url: string; + procedureNames: Set; + remote: Remote; +}; + +export default class RemoteWorker implements Worker +{ + #url: string; + #procedureNames: Set; + #remote: Remote; + + constructor(configuration: Configuration) + { + this.#url = configuration.url; + this.#procedureNames = configuration.procedureNames; + this.#remote = configuration.remote; + } + + get url() { return this.#url; } + + get trustKey() { return undefined; } + + start(): Promise + { + return this.#remote.connect(); + } + + stop(): Promise + { + return this.#remote.disconnect(); + } + + getProcedureNames(): string[] + { + return [...this.#procedureNames.values()]; + } + + hasProcedure(name: string): boolean + { + return this.#procedureNames.has(name); + } + + isHealthy(): Promise + { + return this.#remote.isHealthy(); + } + + getHealth(): Promise> + { + return this.#remote.getHealth(); + } + + run(request: Request): Promise + { + return this.#remote.run(request); + } +} diff --git a/packages/services/src/worker/Worker.ts b/packages/services/src/worker/Worker.ts new file mode 100644 index 00000000..46359467 --- /dev/null +++ b/packages/services/src/worker/Worker.ts @@ -0,0 +1,9 @@ + +import RunnerService from '../RunnerService'; + +interface Worker extends RunnerService +{ + get trustKey(): string | undefined; +} + +export default Worker; diff --git a/packages/services/src/worker/errors/RequestNotTrusted.ts b/packages/services/src/worker/errors/RequestNotTrusted.ts new file mode 100644 index 00000000..27a30d21 --- /dev/null +++ b/packages/services/src/worker/errors/RequestNotTrusted.ts @@ -0,0 +1,10 @@ + +import { Unauthorized } from '@jitar/errors'; + +export default class RequestNotTrusted extends Unauthorized +{ + constructor() + { + super(`Request not trusted`); + } +} diff --git a/packages/services/test/gateway/LocalGateway.spec.ts b/packages/services/test/gateway/LocalGateway.spec.ts new file mode 100644 index 00000000..9efb587f --- /dev/null +++ b/packages/services/test/gateway/LocalGateway.spec.ts @@ -0,0 +1,45 @@ + +import { describe, expect, it } from 'vitest'; + +import InvalidTrustKey from '../../src/gateway/errors/InvalidTrustKey'; + +import { LOCAL_GATEWAYS, REMOTE_WORKERS, VALUES } from './fixtures'; + +const publicGateway = LOCAL_GATEWAYS.PUBLIC; +const protectedGateway = LOCAL_GATEWAYS.PROTECTED; + +const emptyWorker = REMOTE_WORKERS.EMPTY; + +describe('gateway/LocalGateway', () => +{ + describe('.addWorker(worker, trustKey?)', () => + { + it('should add a worker without a trust key to a public gateway', () => + { + const promise = publicGateway.addWorker(emptyWorker); + + expect(promise).resolves.toBeUndefined(); + }); + + it('should add a worker with a valid trust key to a protected gateway', () => + { + const promise = protectedGateway.addWorker(emptyWorker, VALUES.TRUST_KEY); + + expect(promise).resolves.toBeUndefined(); + }); + + it('should not add a worker with an invalid trust key to a protected gateway', () => + { + const promise = protectedGateway.addWorker(emptyWorker, 'INCORRECT_ACCESS_KEY'); + + expect(promise).rejects.toEqual(new InvalidTrustKey()); + }); + + it('should not add a worker with a missing trust key to a protected gateway', () => + { + const promise = protectedGateway.addWorker(emptyWorker); + + expect(promise).rejects.toEqual(new InvalidTrustKey()); + }); + }); +}); diff --git a/packages/services/test/gateway/WorkerBalancer.spec.ts b/packages/services/test/gateway/WorkerBalancer.spec.ts new file mode 100644 index 00000000..21a5452c --- /dev/null +++ b/packages/services/test/gateway/WorkerBalancer.spec.ts @@ -0,0 +1,41 @@ + +import { describe, expect, it } from 'vitest'; + +import { Request, RunModes, Version } from '@jitar/execution'; + +import NoWorkerAvailable from '../../src/gateway/errors/NoWorkerAvailable'; + +import { WORKER_BALANCERS, REMOTE_WORKERS } from './fixtures'; + +const emptyBalancer = WORKER_BALANCERS.EMPTY; +const filledBalancer = WORKER_BALANCERS.FILLED; + +describe('services/WorkerBalancer', () => +{ + describe('.getNextWorker()', () => + { + it('should select workers round robin', async () => + { + const firstSelectedWorker = filledBalancer.getNextWorker(); + const secondSelectedWorker = filledBalancer.getNextWorker(); + const thirdSelectedWorker = filledBalancer.getNextWorker(); + const fourthSelectedWorker = filledBalancer.getNextWorker(); + + expect(firstSelectedWorker).toBe(REMOTE_WORKERS.FIRST); + expect(secondSelectedWorker).toBe(REMOTE_WORKERS.SECOND); + expect(thirdSelectedWorker).toBe(REMOTE_WORKERS.FIRST); + expect(fourthSelectedWorker).toBe(REMOTE_WORKERS.SECOND); + }); + }); + + describe('.run(request)', () => + { + it('should throw a worker not available error', async () => + { + const request = new Request('nonExisting', Version.DEFAULT, new Map(), new Map(), RunModes.NORMAL); + const promise = emptyBalancer.run(request); + + expect(promise).rejects.toEqual(new NoWorkerAvailable('nonExisting')); + }); + }); +}); diff --git a/packages/services/test/gateway/WorkerManager.spec.ts b/packages/services/test/gateway/WorkerManager.spec.ts new file mode 100644 index 00000000..7130ca1e --- /dev/null +++ b/packages/services/test/gateway/WorkerManager.spec.ts @@ -0,0 +1,79 @@ + +import { describe, expect, it } from 'vitest'; + +import { Request, Response, RunModes, StatusCodes, Version, ProcedureNotFound } from '@jitar/execution'; + +import { WORKER_MANAGERS, REMOTE_WORKERS } from './fixtures'; + +const filledManager = WORKER_MANAGERS.FILLED; + +describe('gateway/WorkerManager', () => +{ + describe('.getProcedureNames()', () => + { + it('should get the unique procedure names', () => + { + const procedureNames = filledManager.getProcedureNames(); + expect(procedureNames).toEqual(['first', 'second']); + }); + }); + + describe('.hasProcedure()', () => + { + it('should confirm containing existing procedure names', () => + { + const hasFirst = filledManager.hasProcedure('first'); + expect(hasFirst).toBeTruthy(); + + const hasSecond = filledManager.hasProcedure('second'); + expect(hasSecond).toBeTruthy(); + }); + + it('should deny containing non-existing procedure names', () => + { + const hasThird = filledManager.hasProcedure('third'); + expect(hasThird).toBeFalsy(); + }); + }); + + describe('.addWorker(worker)', () => + { + // The workers are already added in the fixtures + // thus we only need to test the balancers. + + it('should create balancers for added workers', () => + { + const balancers = filledManager.balancers; + expect(balancers.size).toBe(2); + + const firstBalancer = balancers.get('first'); + expect(firstBalancer).toBeDefined(); + expect(firstBalancer?.workers).toEqual([REMOTE_WORKERS.FIRST, REMOTE_WORKERS.SECOND]); + + const secondBalancer = balancers.get('second'); + expect(secondBalancer).toBeDefined(); + expect(secondBalancer?.workers).toEqual([REMOTE_WORKERS.SECOND]); + }); + }); + + describe('.run(request)', () => + { + it('should run an existing procedure', () => + { + const request = new Request('first', Version.DEFAULT, new Map(), new Map(), RunModes.NORMAL); + + const promise = filledManager.run(request); + + expect(promise).resolves.toEqual(new Response(StatusCodes.OK, 'test')); + }); + + it('should not run a non-existing procedure', () => + { + const request = new Request('nonExisting', Version.DEFAULT, new Map(), new Map(), RunModes.NORMAL); + + const promise = filledManager.run(request); + + expect(promise).rejects.toEqual(new ProcedureNotFound('nonExisting')); + }); + }); +}); diff --git a/packages/services/test/gateway/WorkerMonitor.spec.ts b/packages/services/test/gateway/WorkerMonitor.spec.ts new file mode 100644 index 00000000..1345b8c9 --- /dev/null +++ b/packages/services/test/gateway/WorkerMonitor.spec.ts @@ -0,0 +1,29 @@ + +import { describe, expect, it } from 'vitest'; + +import { WORKER_MONITORS, REMOTE_WORKERS } from './fixtures'; + +const monitor = WORKER_MONITORS.EMPTY; + +describe('gateway/WorkerMonitor', () => +{ + it('should remove unhealthy workers', async () => + { + const manager = monitor.workerManager; + manager.addWorker(REMOTE_WORKERS.HEALTHY); + manager.addWorker(REMOTE_WORKERS.UNHEALTHY); + + const beforeWorkers = manager.workers; + expect(beforeWorkers.length).toBe(2); + expect(beforeWorkers[0]).toBe(REMOTE_WORKERS.HEALTHY); + expect(beforeWorkers[1]).toBe(REMOTE_WORKERS.UNHEALTHY); + + monitor.start(); + await new Promise(resolve => setTimeout(resolve, 300)); + monitor.stop(); + + const afterWorkers = manager.workers; + expect(afterWorkers.length).toBe(1); + expect(afterWorkers[0]).toBe(REMOTE_WORKERS.HEALTHY); + }); +}); diff --git a/packages/services/test/gateway/fixtures/index.ts b/packages/services/test/gateway/fixtures/index.ts new file mode 100644 index 00000000..c1852c9d --- /dev/null +++ b/packages/services/test/gateway/fixtures/index.ts @@ -0,0 +1,8 @@ + +export * from './localGateways.fixture'; +export * from './remoteWorkers.fixture'; +export * from './values.fixture'; +export * from './remotes.fixture'; +export * from './workerBalancers.fixture'; +export * from './workerManagers.fixture'; +export * from './workerMonitors.fixture'; diff --git a/packages/services/test/gateway/fixtures/localGateways.fixture.ts b/packages/services/test/gateway/fixtures/localGateways.fixture.ts new file mode 100644 index 00000000..e171ab30 --- /dev/null +++ b/packages/services/test/gateway/fixtures/localGateways.fixture.ts @@ -0,0 +1,16 @@ + +import LocalGateway from '../../../src/gateway/LocalGateway'; + +import { VALUES } from './values.fixture'; + +const url = VALUES.URL; +const trustKey = VALUES.TRUST_KEY; + +const publicGateway = new LocalGateway({ url }); +const protectedGateway = new LocalGateway({ url, trustKey}); + +export const LOCAL_GATEWAYS = +{ + PUBLIC: publicGateway, + PROTECTED: protectedGateway +}; diff --git a/packages/services/test/gateway/fixtures/remoteWorkers.fixture.ts b/packages/services/test/gateway/fixtures/remoteWorkers.fixture.ts new file mode 100644 index 00000000..1c0d1826 --- /dev/null +++ b/packages/services/test/gateway/fixtures/remoteWorkers.fixture.ts @@ -0,0 +1,45 @@ + +import RemoteWorker from '../../../src/worker/RemoteWorker'; + +import { VALUES } from './values.fixture'; +import { REMOTES } from './remotes.fixture'; + +class HealthyWorker extends RemoteWorker +{ + async isHealthy(): Promise + { + return true; + } +} + +class UnhealthyWorker extends RemoteWorker +{ + async isHealthy(): Promise + { + return false; + } +} + +const url = VALUES.URL; +const remote = REMOTES.DUMMY; + +const noProcedureNames = new Set(); +const emptyWorker = new RemoteWorker({ url, procedureNames: noProcedureNames, remote }); + +const firstProcedureNames = new Set(['first']); +const firstWorker = new RemoteWorker({ url, procedureNames: firstProcedureNames, remote }); + +const secondProcedureNames = new Set(['first', 'second']); +const secondWorker = new RemoteWorker({ url, procedureNames: secondProcedureNames, remote }); + +const healthyWorker = new HealthyWorker({ url, procedureNames: noProcedureNames, remote }); +const unhealthyWorker = new UnhealthyWorker({ url, procedureNames: noProcedureNames, remote }); + +export const REMOTE_WORKERS = +{ + EMPTY: emptyWorker, + FIRST: firstWorker, + SECOND: secondWorker, + HEALTHY: healthyWorker, + UNHEALTHY: unhealthyWorker +}; diff --git a/packages/services/test/gateway/fixtures/remotes.fixture.ts b/packages/services/test/gateway/fixtures/remotes.fixture.ts new file mode 100644 index 00000000..0ef87e97 --- /dev/null +++ b/packages/services/test/gateway/fixtures/remotes.fixture.ts @@ -0,0 +1,52 @@ + +import { NotImplemented } from '@jitar/errors'; +import { Request, Response, StatusCodes } from '@jitar/execution'; +import { File } from '@jitar/sourcing'; + +import Remote from '../../../src/Remote'; + +class DummyRemote implements Remote +{ + connect(): Promise + { + throw new NotImplemented(); + } + + disconnect(): Promise + { + throw new NotImplemented(); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + provide(filename: string): Promise + { + throw new NotImplemented(); + } + + isHealthy(): Promise + { + throw new NotImplemented(); + } + + getHealth(): Promise> + { + throw new NotImplemented(); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + addWorker(workerUrl: string, procedureNames: string[], trustKey?: string): Promise + { + throw new NotImplemented(); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + async run(request: Request): Promise + { + return new Response(StatusCodes.OK, 'test'); + } +} + +export const REMOTES = +{ + DUMMY: new DummyRemote() +}; diff --git a/packages/services/test/gateway/fixtures/values.fixture.ts b/packages/services/test/gateway/fixtures/values.fixture.ts new file mode 100644 index 00000000..d292da82 --- /dev/null +++ b/packages/services/test/gateway/fixtures/values.fixture.ts @@ -0,0 +1,6 @@ + +export const VALUES = +{ + URL: 'http://localhost:80', + TRUST_KEY: 'MY_TRUSTED_ACCESS_KEY' +}; diff --git a/packages/services/test/gateway/fixtures/workerBalancers.fixture.ts b/packages/services/test/gateway/fixtures/workerBalancers.fixture.ts new file mode 100644 index 00000000..50dfd8c4 --- /dev/null +++ b/packages/services/test/gateway/fixtures/workerBalancers.fixture.ts @@ -0,0 +1,16 @@ + +import WorkerBalancer from '../../../src/gateway/WorkerBalancer'; + +import { REMOTE_WORKERS } from './remoteWorkers.fixture'; + +const emptyBalancer = new WorkerBalancer(); + +const filledBalancer = new WorkerBalancer(); +filledBalancer.addWorker(REMOTE_WORKERS.FIRST); +filledBalancer.addWorker(REMOTE_WORKERS.SECOND); + +export const WORKER_BALANCERS = +{ + EMPTY: emptyBalancer, + FILLED: filledBalancer +}; diff --git a/packages/services/test/gateway/fixtures/workerManagers.fixture.ts b/packages/services/test/gateway/fixtures/workerManagers.fixture.ts new file mode 100644 index 00000000..f6a6043b --- /dev/null +++ b/packages/services/test/gateway/fixtures/workerManagers.fixture.ts @@ -0,0 +1,13 @@ + +import WorkerManager from '../../../src/gateway/WorkerManager'; + +import { REMOTE_WORKERS } from './remoteWorkers.fixture'; + +const filledManager = new WorkerManager(); +filledManager.addWorker(REMOTE_WORKERS.FIRST); +filledManager.addWorker(REMOTE_WORKERS.SECOND); + +export const WORKER_MANAGERS = +{ + FILLED: filledManager +}; diff --git a/packages/services/test/gateway/fixtures/workerMonitors.fixture.ts b/packages/services/test/gateway/fixtures/workerMonitors.fixture.ts new file mode 100644 index 00000000..2efc5661 --- /dev/null +++ b/packages/services/test/gateway/fixtures/workerMonitors.fixture.ts @@ -0,0 +1,11 @@ + +import WorkerManager from '../../../src/gateway/WorkerManager'; +import WorkerMonitor from '../../../src/gateway/WorkerMonitor'; + +const emptyManager = new WorkerManager(); +const emptyMonitor = new WorkerMonitor(emptyManager, 200); + +export const WORKER_MONITORS = +{ + EMPTY: emptyMonitor +}; diff --git a/packages/services/test/repository/LocalRepository.spec.ts b/packages/services/test/repository/LocalRepository.spec.ts new file mode 100644 index 00000000..8fa67f58 --- /dev/null +++ b/packages/services/test/repository/LocalRepository.spec.ts @@ -0,0 +1,36 @@ + +import { describe, expect, it } from 'vitest'; + +import { FileNotFound } from '@jitar/sourcing'; + +import { LOCAL_REPOSITORIES, FILENAMES, FILES } from './fixtures'; + +const fileRepository = LOCAL_REPOSITORIES.FILE; +const webRepository = LOCAL_REPOSITORIES.WEB; + +describe('repository/LocalRepository', () => +{ + describe('.provide(filename)', () => + { + it('should provide a existing file', () => + { + const promise = fileRepository.provide(FILENAMES.PNG); + + expect(promise).resolves.toEqual(FILES.PNG); + }); + + it('should not provide a non-existing file', () => + { + const promise = fileRepository.provide(FILENAMES.TXT); + + expect(promise).rejects.toEqual(new FileNotFound(FILENAMES.TXT)); + }); + + it('should provide index file when file not found', () => + { + const promise = webRepository.provide(FILENAMES.TXT); + + expect(promise).resolves.toEqual(FILES.HTML); + }); + }); +}); diff --git a/packages/services/test/repository/fixtures/filenames.fixture.ts b/packages/services/test/repository/fixtures/filenames.fixture.ts new file mode 100644 index 00000000..1a0adc78 --- /dev/null +++ b/packages/services/test/repository/fixtures/filenames.fixture.ts @@ -0,0 +1,7 @@ + +export const FILENAMES = +{ + HTML: 'index.html', + PNG: 'logo.png', + TXT: 'non-existing.txt', +}; diff --git a/packages/services/test/repository/fixtures/files.fixtures.ts b/packages/services/test/repository/fixtures/files.fixtures.ts new file mode 100644 index 00000000..36c37f3f --- /dev/null +++ b/packages/services/test/repository/fixtures/files.fixtures.ts @@ -0,0 +1,8 @@ + +import { File } from '@jitar/sourcing'; + +export const FILES = +{ + HTML: new File('index.html', 'text/html', Buffer.from('Hello, world!')), + PNG: new File('logo.png', 'image/png', Buffer.from('Fake image')) +}; diff --git a/packages/services/test/repository/fixtures/index.ts b/packages/services/test/repository/fixtures/index.ts new file mode 100644 index 00000000..cd51605e --- /dev/null +++ b/packages/services/test/repository/fixtures/index.ts @@ -0,0 +1,5 @@ + +export * from './filenames.fixture'; +export * from './files.fixtures'; +export * from './localRepositories.fixture'; +export * from './sourcingManager.fixture'; diff --git a/packages/services/test/repository/fixtures/localRepositories.fixture.ts b/packages/services/test/repository/fixtures/localRepositories.fixture.ts new file mode 100644 index 00000000..5f689283 --- /dev/null +++ b/packages/services/test/repository/fixtures/localRepositories.fixture.ts @@ -0,0 +1,18 @@ + +import LocalRepository from '../../../src/repository/LocalRepository'; + +import { sourcingManager } from './sourcingManager.fixture'; + +const url = 'http://localhost:80'; +const assets = new Set(['index.html', 'logo.png']); +const indexFilename = 'index.html'; +const serveIndexOnNotFound = true; + +const fileRepository = new LocalRepository({ url, assets, sourcingManager }); +const webRepository = new LocalRepository({ url, assets, sourcingManager, indexFilename, serveIndexOnNotFound }); + +export const LOCAL_REPOSITORIES = +{ + FILE: fileRepository, + WEB: webRepository +}; diff --git a/packages/services/test/repository/fixtures/sourcingManager.fixture.ts b/packages/services/test/repository/fixtures/sourcingManager.fixture.ts new file mode 100644 index 00000000..96d4b758 --- /dev/null +++ b/packages/services/test/repository/fixtures/sourcingManager.fixture.ts @@ -0,0 +1,76 @@ + +import { NotImplemented } from '@jitar/errors'; +import { File, FileManager, FileNotFound, SourcingManager } from '@jitar/sourcing'; + +import { FILENAMES } from './filenames.fixture'; +import { FILES } from './files.fixtures'; + +class DummyFileManager implements FileManager +{ + getRootLocation(): string + { + throw new NotImplemented(); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + getAbsoluteLocation(filename: string): string + { + throw new NotImplemented(); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + getRelativeLocation(filename: string): string + { + throw new NotImplemented(); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + getType(filename: string): Promise + { + throw new NotImplemented(); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + getContent(filename: string): Promise + { + throw new NotImplemented(); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + exists(filename: string): Promise + { + throw new NotImplemented(); + } + + async read(filename: string): Promise + { + switch (filename) + { + case FILENAMES.HTML: return FILES.HTML; + case FILENAMES.PNG: return FILES.PNG; + default: throw new FileNotFound(filename); + } + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + write(filename: string, content: string): Promise + { + throw new NotImplemented(); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + delete(filename: string): Promise + { + throw new NotImplemented(); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + filter(pattern: string): Promise + { + throw new NotImplemented(); + } +} + +const fileManager = new DummyFileManager(); + +export const sourcingManager = new SourcingManager(fileManager); diff --git a/packages/services/test/worker/LocalWorker.spec.ts b/packages/services/test/worker/LocalWorker.spec.ts new file mode 100644 index 00000000..104a2d5b --- /dev/null +++ b/packages/services/test/worker/LocalWorker.spec.ts @@ -0,0 +1,69 @@ + +import { describe, expect, it } from 'vitest'; + +import { Request, Version, ProcedureNotFound, RunModes } from '@jitar/execution'; + +import RequestNotTrusted from '../../src/worker/errors/RequestNotTrusted'; + +import { LOCAL_WORKERS, VALUES } from './fixtures'; + +const publicWorker = LOCAL_WORKERS.PUBLIC; +const protectedWorker = LOCAL_WORKERS.PROTECTED; + +describe('worker/LocalWorker', () => +{ + describe('.run(request)', () => + { + it('should run a public procedure ', async () => + { + const headers = new Map().set(VALUES.TRUST_KEY_HEADER, VALUES.TRUST_KEY); + const request = new Request('public', Version.DEFAULT, new Map(), headers, RunModes.NORMAL); + + const response = await publicWorker.run(request); + + expect(response.result).toBe('public'); + }); + + it('should run a protected procedure with valid trust key', async () => + { + const headers = new Map().set(VALUES.TRUST_KEY_HEADER, VALUES.TRUST_KEY); + const request = new Request('protected', Version.DEFAULT, new Map(), headers, RunModes.NORMAL); + + const response = await protectedWorker.run(request); + + expect(response.result).toBe('protected'); + }); + + it('should not run a non-existing procedure', async () => + { + const request = new Request('nonExisting', Version.DEFAULT, new Map(), new Map(), RunModes.NORMAL); + + const promise = protectedWorker.run(request); + + expect(promise).rejects.toEqual(new ProcedureNotFound('nonExisting')); + }); + + it('should not run a protected procedure with invalid trust key', async () => + { + const headers = new Map().set(VALUES.TRUST_KEY_HEADER, 'invalid'); + const request = new Request('protected', Version.DEFAULT, new Map(), headers, RunModes.NORMAL); + + const promise = protectedWorker.run(request); + + expect(promise).rejects.toEqual(new RequestNotTrusted()); + }); + + it('should not run a protected procedure without trust key', async () => + { + const request = new Request('protected', Version.DEFAULT, new Map(), new Map(), RunModes.NORMAL); + + const promise = protectedWorker.run(request); + + expect(promise).rejects.toEqual(new RequestNotTrusted()); + }); + + // TODO: Add tests for remote execution + + // TODO: Add tests for (de)serialization + }); +}); diff --git a/packages/services/test/worker/fixtures/executionManager.fixture.ts b/packages/services/test/worker/fixtures/executionManager.fixture.ts new file mode 100644 index 00000000..ba256595 --- /dev/null +++ b/packages/services/test/worker/fixtures/executionManager.fixture.ts @@ -0,0 +1,8 @@ + +import { ExecutionManager } from '@jitar/execution'; + +import { SEGMENTS } from './segments.fixture'; + +export const executionManager = new ExecutionManager(); +executionManager.addSegment(SEGMENTS.FIRST); +executionManager.addSegment(SEGMENTS.SECOND); diff --git a/packages/services/test/worker/fixtures/index.ts b/packages/services/test/worker/fixtures/index.ts new file mode 100644 index 00000000..f6103a63 --- /dev/null +++ b/packages/services/test/worker/fixtures/index.ts @@ -0,0 +1,6 @@ + +export * from './executionManager.fixture'; +export * from './localWorkers.fixture'; +export * from './procedures.fixture'; +export * from './segments.fixture'; +export * from './values.fixture'; diff --git a/packages/services/test/worker/fixtures/localWorkers.fixture.ts b/packages/services/test/worker/fixtures/localWorkers.fixture.ts new file mode 100644 index 00000000..5445ec35 --- /dev/null +++ b/packages/services/test/worker/fixtures/localWorkers.fixture.ts @@ -0,0 +1,17 @@ + +import LocalWorker from '../../../src/worker/LocalWorker'; + +import { executionManager } from './executionManager.fixture'; +import { VALUES } from './values.fixture'; + +const url = VALUES.URL; +const trustKey = VALUES.TRUST_KEY; + +const publicWorker = new LocalWorker({ url, executionManager}); +const protectedWorker = new LocalWorker({ url, trustKey, executionManager}); + +export const LOCAL_WORKERS = +{ + PUBLIC: publicWorker, + PROTECTED: protectedWorker +}; diff --git a/packages/services/test/worker/fixtures/procedures.fixture.ts b/packages/services/test/worker/fixtures/procedures.fixture.ts new file mode 100644 index 00000000..72d7ee6b --- /dev/null +++ b/packages/services/test/worker/fixtures/procedures.fixture.ts @@ -0,0 +1,14 @@ + +import { Procedure, Implementation, Version, AccessLevels } from '@jitar/execution'; + +const publicProcedure = new Procedure('public'); +publicProcedure.addImplementation(new Implementation(Version.DEFAULT, AccessLevels.PUBLIC, [], () => 'public')); + +const protectedProcedure = new Procedure('protected'); +protectedProcedure.addImplementation(new Implementation(Version.DEFAULT, AccessLevels.PROTECTED, [], () => 'protected')); + +export const PROCEDURES = +{ + PUBLIC: publicProcedure, + PROTECTED: protectedProcedure +}; diff --git a/packages/services/test/worker/fixtures/segments.fixture.ts b/packages/services/test/worker/fixtures/segments.fixture.ts new file mode 100644 index 00000000..338ddca8 --- /dev/null +++ b/packages/services/test/worker/fixtures/segments.fixture.ts @@ -0,0 +1,16 @@ + +import { Segment } from '@jitar/execution'; + +import { PROCEDURES } from './procedures.fixture'; + +const firstSegment = new Segment('first' ); +firstSegment.addProcedure(PROCEDURES.PUBLIC); + +const secondSegment = new Segment('second'); +secondSegment.addProcedure(PROCEDURES.PROTECTED); + +export const SEGMENTS = +{ + FIRST: firstSegment, + SECOND: secondSegment +}; diff --git a/packages/services/test/worker/fixtures/values.fixture.ts b/packages/services/test/worker/fixtures/values.fixture.ts new file mode 100644 index 00000000..0235a7ba --- /dev/null +++ b/packages/services/test/worker/fixtures/values.fixture.ts @@ -0,0 +1,7 @@ + +export const VALUES = +{ + URL: 'http://localhost:80', + TRUST_KEY: 'MY_TRUSTED_ACCESS_KEY', + TRUST_KEY_HEADER: 'x-jitar-trust-key' +}; diff --git a/packages/services/tsconfig.json b/packages/services/tsconfig.json new file mode 100644 index 00000000..e66fac12 --- /dev/null +++ b/packages/services/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "rootDir": "./src/", + "moduleResolution": "node", + "declaration": true, + "outDir": "./dist", + "removeComments": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "skipLibCheck": true + }, + "exclude": [ + "vite.config.ts", + "node_modules", + "dist", + "test" + ] +} \ No newline at end of file diff --git a/packages/services/vite.config.ts b/packages/services/vite.config.ts new file mode 100644 index 00000000..f5182e1f --- /dev/null +++ b/packages/services/vite.config.ts @@ -0,0 +1,10 @@ +// vite.config.ts +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + coverage: { + provider: 'v8' + }, + }, +}); diff --git a/packages/sourcing/CHANGELOG.md b/packages/sourcing/CHANGELOG.md new file mode 100644 index 00000000..80f29432 --- /dev/null +++ b/packages/sourcing/CHANGELOG.md @@ -0,0 +1,4 @@ + +# Changelog + +This package doesn't keep a changelog. See the changelog in the [github repository](https://github.com/MaskingTechnology/jitar/blob/main/CHANGELOG.md) \ No newline at end of file diff --git a/packages/sourcing/README.md b/packages/sourcing/README.md new file mode 100644 index 00000000..b5917229 --- /dev/null +++ b/packages/sourcing/README.md @@ -0,0 +1,9 @@ + +# Jitar Configuration + +This package provides application sourcing (file and modules) for the [Jitar](https://jitar.dev) runtime. + +For more information about Jitar: + +* [Visit our website](https://jitar.dev) +* [Read the documentation](https://docs.jitar.dev). diff --git a/packages/sourcing/package.json b/packages/sourcing/package.json new file mode 100644 index 00000000..d74d57bd --- /dev/null +++ b/packages/sourcing/package.json @@ -0,0 +1,24 @@ +{ + "name": "@jitar/sourcing", + "version": "0.7.4", + "description": "Sourcing library for the Jitar runtime.", + "author": "Masking Technology (https://jitar.dev)", + "license": "MIT", + "type": "module", + "types": "dist/index.d.ts", + "exports": "./dist/index.js", + "private": true, + "scripts": { + "test": "vitest run", + "test-coverage": "vitest run --coverage", + "lint": "eslint . --ext .ts", + "build": "tsc -p tsconfig.json", + "clean": "rm -rf dist" + }, + "dependencies": { + "@jitar/errors": "*", + "fs-extra": "^11.2.0", + "glob": "11.0.0", + "mime-types": "^2.1.35" + } +} diff --git a/packages/server-nodejs/src/utils/LocalFileManager.ts b/packages/sourcing/src/LocalFileManager.ts similarity index 74% rename from packages/server-nodejs/src/utils/LocalFileManager.ts rename to packages/sourcing/src/LocalFileManager.ts index 816dcf19..1af4c5a3 100644 --- a/packages/server-nodejs/src/utils/LocalFileManager.ts +++ b/packages/sourcing/src/LocalFileManager.ts @@ -4,7 +4,9 @@ import { glob } from 'glob'; import mime from 'mime-types'; import path from 'path'; -import { FileManager, File, FileNotFound } from '@jitar/runtime'; +import FileNotFound from './errors/FileNotFound'; +import type FileManager from './interfaces/FileManager'; +import File from './models/File'; export default class LocalFileManager implements FileManager { @@ -53,6 +55,13 @@ export default class LocalFileManager implements FileManager return fs.readFile(location); } + async exists(filename: string): Promise + { + const location = this.getAbsoluteLocation(filename); + + return fs.exists(location); + } + async read(filename: string): Promise { const rootPath = this.getRootLocation(); @@ -100,32 +109,4 @@ export default class LocalFileManager implements FileManager return glob(`${location}/${pattern}`); } - - async getWorkerSegmentFiles(): Promise - { - return this.filter('**/*.segment.worker.js'); - } - - async getRepositorySegmentFiles(): Promise - { - return this.filter('**/*.segment.repository.js'); - } - - async getAssetFiles(patterns: string[]): Promise - { - const promises = patterns.map(pattern => this.filter(pattern)); - const assetFiles = (await Promise.all(promises)).flat(); - - return assetFiles - .map(filename => this.getRelativeLocation(filename)) - .filter(filename => this.#isGeneratedFile(filename) === false); - } - - #isGeneratedFile(filename: string): boolean - { - return filename.endsWith('.local.js') - || filename.endsWith('.worker.js') - || filename.endsWith('.repository.js') - || filename.endsWith('.remote.js'); - } } diff --git a/packages/sourcing/src/SourcingManager.ts b/packages/sourcing/src/SourcingManager.ts new file mode 100644 index 00000000..337c5d19 --- /dev/null +++ b/packages/sourcing/src/SourcingManager.ts @@ -0,0 +1,53 @@ + +import type File from './models/File'; +import type FileManager from './interfaces/FileManager'; +import ModuleNotLoaded from './errors/ModuleNotLoaded'; +import type Module from './types/Module'; + +export default class SourceManager +{ + #fileManager: FileManager; + + constructor(fileManager: FileManager) + { + this.#fileManager = fileManager; + } + + async filter(...patterns: string[]): Promise + { + const files = await Promise.all(patterns.map(pattern => this.#fileManager.filter(pattern))); + + return files.flat().map(file => this.#fileManager.getRelativeLocation(file)); + } + + async exists(filename: string): Promise + { + return this.#fileManager.exists(filename); + } + + async read(filename: string): Promise + { + return this.#fileManager.read(filename); + } + + async import(filename: string): Promise + { + // If the specifier is an absolute path, we need to convert it to a path + // relative to the cache folder. + + const specifier = filename.startsWith('/') + ? this.#fileManager.getAbsoluteLocation(`.${filename}`) + : this.#fileManager.getAbsoluteLocation(filename); + + try + { + return await import(specifier); + } + catch (error: unknown) + { + const message = error instanceof Error ? error.message : String(error); + + throw new ModuleNotLoaded(specifier, message); + } + } +} diff --git a/packages/sourcing/src/definitions/Files.ts b/packages/sourcing/src/definitions/Files.ts new file mode 100644 index 00000000..944cf9e0 --- /dev/null +++ b/packages/sourcing/src/definitions/Files.ts @@ -0,0 +1,8 @@ + +const Files = +{ + MODULE_PATTERN: '**/*.js', + SEGMENT_PATTERN: '**/*.segment.json' +}; + +export default Files; diff --git a/packages/runtime/src/errors/FileNotFound.ts b/packages/sourcing/src/errors/FileNotFound.ts similarity index 50% rename from packages/runtime/src/errors/FileNotFound.ts rename to packages/sourcing/src/errors/FileNotFound.ts index 2a101b45..bb886463 100644 --- a/packages/runtime/src/errors/FileNotFound.ts +++ b/packages/sourcing/src/errors/FileNotFound.ts @@ -1,9 +1,5 @@ -import { Loadable } from '@jitar/serialization'; - -import NotFound from './generic/NotFound.js'; - -export default class FileNotFound extends NotFound +export default class FileNotFound extends Error { #filename: string; @@ -16,5 +12,3 @@ export default class FileNotFound extends NotFound get filename() { return this.#filename; } } - -(FileNotFound as Loadable).source = 'RUNTIME_ERROR_LOCATION'; diff --git a/packages/runtime/src/errors/ModuleNotLoaded.ts b/packages/sourcing/src/errors/ModuleNotLoaded.ts similarity index 62% rename from packages/runtime/src/errors/ModuleNotLoaded.ts rename to packages/sourcing/src/errors/ModuleNotLoaded.ts index 3f55870b..09490357 100644 --- a/packages/runtime/src/errors/ModuleNotLoaded.ts +++ b/packages/sourcing/src/errors/ModuleNotLoaded.ts @@ -1,9 +1,5 @@ -import { Loadable } from '@jitar/serialization'; - -import ServerError from './generic/ServerError.js'; - -export default class ModuleNotLoaded extends ServerError +export default class ModuleNotLoaded extends Error { #url: string; #reason?: string; @@ -22,5 +18,3 @@ export default class ModuleNotLoaded extends ServerError get reason() { return this.#reason; } } - -(ModuleNotLoaded as Loadable).source = 'RUNTIME_ERROR_LOCATION'; diff --git a/packages/sourcing/src/index.ts b/packages/sourcing/src/index.ts new file mode 100644 index 00000000..ab9940b4 --- /dev/null +++ b/packages/sourcing/src/index.ts @@ -0,0 +1,10 @@ + +export { default as Files } from './definitions/Files'; +export { default as FileNotFound } from './errors/FileNotFound'; +export { default as ModuleNotLoaded } from './errors/ModuleNotLoaded'; +export { default as File } from './models/File'; +export { default as FileManager } from './interfaces/FileManager'; +export { default as Module } from './types/Module'; + +export { default as LocalFileManager } from './LocalFileManager'; +export { default as SourcingManager } from './SourcingManager'; diff --git a/packages/runtime/src/interfaces/FileManager.ts b/packages/sourcing/src/interfaces/FileManager.ts similarity index 84% rename from packages/runtime/src/interfaces/FileManager.ts rename to packages/sourcing/src/interfaces/FileManager.ts index ee6f5292..d49ea258 100644 --- a/packages/runtime/src/interfaces/FileManager.ts +++ b/packages/sourcing/src/interfaces/FileManager.ts @@ -1,5 +1,5 @@ -import File from '../models/File.js'; +import type File from '../models/File'; interface FileManager { @@ -13,6 +13,8 @@ interface FileManager getContent(filename: string): Promise; + exists(filename: string): Promise; + read(filename: string): Promise; write(filename: string, content: string): Promise; diff --git a/packages/runtime/src/models/File.ts b/packages/sourcing/src/models/File.ts similarity index 100% rename from packages/runtime/src/models/File.ts rename to packages/sourcing/src/models/File.ts diff --git a/packages/runtime/src/types/Module.ts b/packages/sourcing/src/types/Module.ts similarity index 100% rename from packages/runtime/src/types/Module.ts rename to packages/sourcing/src/types/Module.ts diff --git a/packages/sourcing/test/dummy.spec.ts b/packages/sourcing/test/dummy.spec.ts new file mode 100644 index 00000000..2489f1ee --- /dev/null +++ b/packages/sourcing/test/dummy.spec.ts @@ -0,0 +1,12 @@ + +import { describe, expect, it } from 'vitest'; + +describe('dummy', () => +{ + // TODO: Add real tests + + it('should not complain about missing tests', async () => + { + expect(true).toBeTruthy(); + }); +}); diff --git a/examples/concepts/node-client/tsconfig.json b/packages/sourcing/tsconfig.json similarity index 61% rename from examples/concepts/node-client/tsconfig.json rename to packages/sourcing/tsconfig.json index 387be42e..4ca2247d 100644 --- a/examples/concepts/node-client/tsconfig.json +++ b/packages/sourcing/tsconfig.json @@ -1,15 +1,21 @@ { - "compilerOptions": - { + "compilerOptions": { "target": "es2022", "module": "es2022", "rootDir": "./src/", "moduleResolution": "node", + "declaration": true, "outDir": "./dist", "removeComments": true, + "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "strict": true, "skipLibCheck": true }, - "exclude": ["cache", "dist", "node_modules"] -} + "exclude": [ + "vite.config.ts", + "node_modules", + "dist", + "test" + ] +} \ No newline at end of file diff --git a/packages/sourcing/vite.config.ts b/packages/sourcing/vite.config.ts new file mode 100644 index 00000000..f5182e1f --- /dev/null +++ b/packages/sourcing/vite.config.ts @@ -0,0 +1,10 @@ +// vite.config.ts +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + coverage: { + provider: 'v8' + }, + }, +}); diff --git a/packages/validation/CHANGELOG.md b/packages/validation/CHANGELOG.md new file mode 100644 index 00000000..80f29432 --- /dev/null +++ b/packages/validation/CHANGELOG.md @@ -0,0 +1,4 @@ + +# Changelog + +This package doesn't keep a changelog. See the changelog in the [github repository](https://github.com/MaskingTechnology/jitar/blob/main/CHANGELOG.md) \ No newline at end of file diff --git a/packages/validation/README.md b/packages/validation/README.md new file mode 100644 index 00000000..60479076 --- /dev/null +++ b/packages/validation/README.md @@ -0,0 +1,9 @@ + +# Jitar Middleware + +This package provides data validation for the [Jitar](https://jitar.dev) runtime. + +For more information about Jitar: + +* [Visit our website](https://jitar.dev) +* [Read the documentation](https://docs.jitar.dev). diff --git a/packages/validation/package.json b/packages/validation/package.json new file mode 100644 index 00000000..6d90a69f --- /dev/null +++ b/packages/validation/package.json @@ -0,0 +1,22 @@ +{ + "name": "@jitar/validation", + "version": "0.7.4", + "description": "Validation library for the Jitar runtime.", + "author": "Masking Technology (https://jitar.dev)", + "license": "MIT", + "type": "module", + "types": "dist/index.d.ts", + "exports": "./dist/index.js", + "private": true, + "scripts": { + "test": "vitest run", + "test-coverage": "vitest run --coverage", + "lint": "eslint . --ext .ts", + "build": "tsc -p tsconfig.json", + "clean": "rm -rf dist" + }, + "dependencies": { + "@jitar/errors": "*", + "@jitar/execution": "*" + } +} diff --git a/packages/validation/src/Validator.ts b/packages/validation/src/Validator.ts new file mode 100644 index 00000000..a930ff3e --- /dev/null +++ b/packages/validation/src/Validator.ts @@ -0,0 +1,174 @@ + +import ValidationScheme, { FieldValidation, PrimitiveValidation, GroupValidation, ListValidation } from './types/ValidationScheme'; +import ValidationResult from './types/ValidationResult'; + +type Data = Record; + +export default class Validator +{ + #strict: boolean; + + public constructor(strict: boolean = true) + { + this.#strict = strict; + } + + validate(data: Data, scheme: ValidationScheme): ValidationResult + { + const errors: string[] = []; + + this.#validateData('', data, scheme, errors); + + const valid = errors.length === 0; + + return { valid, errors }; + } + + #validateData(key: string, data: Data, scheme: ValidationScheme, errors: string[]): void + { + if (this.#strict) + { + this.#validateKeys(key, data, scheme, errors); + } + + this.#validateValues(key, data, scheme, errors); + } + + #validateKeys(key: string, data: Data, scheme: ValidationScheme, errors: string[]): void + { + const dataKeys = Object.keys(data); + const schemeKeys = Object.keys(scheme); + + for (const dataKey of dataKeys) + { + if (schemeKeys.includes(dataKey) === false) + { + const absoluteKey = this.#composeKey(key, dataKey); + + errors.push(`Unknown field '${absoluteKey}'`); + } + } + } + + #validateValues(key: string, data: Data, scheme: ValidationScheme, errors: string[]): void + { + const valueKeys = Object.keys(scheme); + + for (const valueKey of valueKeys) + { + const absoluteKey = this.#composeKey(key, valueKey); + const fieldScheme = scheme[valueKey]; + const value = data[valueKey]; + + this.#validateValue(absoluteKey, value, fieldScheme, errors); + } + } + + #validateValue(key: string, value: unknown, scheme: FieldValidation, errors: string[]): void + { + if (value === undefined) + { + if (scheme.required === true) + { + errors.push(`Field '${key}' is required`); + } + + return; + } + + switch (scheme.type) + { + case 'string': + return this.#validateString(key, value, scheme, errors); + case 'integer': + return this.#validateInteger(key, value, scheme, errors); + case 'real': + return this.#validateReal(key, value, scheme, errors); + case 'boolean': + return this.#validateBoolean(key, value, scheme, errors); + case 'url': + return this.#validateUrl(key, value, scheme, errors); + case 'group': + return this.#validateGroup(key, value, scheme, errors); + case 'list': + return this.#validateList(key, value, scheme, errors); + } + } + + #validateString(key: string, value: unknown, scheme: PrimitiveValidation, errors: string[]): void + { + if (typeof value !== 'string') + { + errors.push(`Field '${key}' is not a string`); + } + } + + #validateInteger(key: string, value: unknown, scheme: PrimitiveValidation, errors: string[]): void + { + if (typeof value !== 'number' || Number.isInteger(value) === false) + { + errors.push(`Field '${key}' is not an integer`); + } + } + + #validateReal(key: string, value: unknown, scheme: PrimitiveValidation, errors: string[]): void + { + if (typeof value !== 'number') + { + errors.push(`Field '${key}' is not a real number`); + } + } + + #validateBoolean(key: string, value: unknown, scheme: PrimitiveValidation, errors: string[]): void + { + if (typeof value !== 'boolean') + { + errors.push(`Field '${key}' is not a boolean`); + } + } + + #validateUrl(key: string, value: unknown, scheme: PrimitiveValidation, errors: string[]): void + { + if (typeof value !== 'string' || value.startsWith('http') === false) + { + errors.push(`Field '${key}' is not a valid URL`); + } + } + + #validateGroup(key: string, value: unknown, scheme: GroupValidation, errors: string[]): void + { + if (typeof value !== 'object') + { + errors.push(`Field '${key}' is not an object`); + + return; + } + + this.#validateData(key, value as Record, scheme.fields, errors); + } + + #validateList(key: string, value: unknown, scheme: ListValidation, errors: string[]): void + { + if (!Array.isArray(value)) + { + errors.push(`Field '${key}' is not a list`); + + return; + } + + const data = value as unknown[]; + + for (const itemIndex in data) + { + const itemKey = this.#composeKey(key, itemIndex); + const itemValue = data[itemIndex]; + + this.#validateValue(itemKey, itemValue, scheme.items, errors); + } + } + + #composeKey(parent: string, key: string): string + { + return parent === '' ? key : `${parent}.${key}`; + } +} diff --git a/packages/validation/src/index.ts b/packages/validation/src/index.ts new file mode 100644 index 00000000..5e322df4 --- /dev/null +++ b/packages/validation/src/index.ts @@ -0,0 +1,4 @@ + +export { default as ValidationScheme } from './types/ValidationScheme'; +export { default as ValidationResult } from './types/ValidationResult'; +export { default as Validator } from './Validator'; diff --git a/packages/validation/src/types/ValidationResult.ts b/packages/validation/src/types/ValidationResult.ts new file mode 100644 index 00000000..dac89677 --- /dev/null +++ b/packages/validation/src/types/ValidationResult.ts @@ -0,0 +1,8 @@ + +type ValidationResult = +{ + valid: boolean; + errors: string[]; +}; + +export default ValidationResult; diff --git a/packages/validation/src/types/ValidationScheme.ts b/packages/validation/src/types/ValidationScheme.ts new file mode 100644 index 00000000..a3221dbe --- /dev/null +++ b/packages/validation/src/types/ValidationScheme.ts @@ -0,0 +1,26 @@ + +export type FieldValidation = PrimitiveValidation | GroupValidation | ListValidation; + +export type PrimitiveValidation = +{ + type: 'string' | 'integer' | 'real' | 'boolean' | 'url'; + required?: boolean; +}; + +export type GroupValidation = +{ + type: 'group'; + required?: boolean; + fields: Record; +} + +export type ListValidation = +{ + type: 'list'; + required?: boolean; + items: PrimitiveValidation; +}; + +type ValidationScheme = Record; + +export default ValidationScheme; diff --git a/packages/validation/test/Validator.spec.ts b/packages/validation/test/Validator.spec.ts new file mode 100644 index 00000000..a3eaa2ff --- /dev/null +++ b/packages/validation/test/Validator.spec.ts @@ -0,0 +1,344 @@ + +import { describe, expect, it } from 'vitest'; + +import { Validator } from '../src'; + +import { VALIDATION_SCHEMES, VALUES } from './fixtures'; + +describe('Validator', () => +{ + describe('String values', () => + { + it('should accept a valid string', () => + { + const validator = new Validator(); + const data = { string: 'string' }; + const scheme = VALIDATION_SCHEMES.STRING; + + const result = validator.validate(data, scheme); + + expect(result.valid).toBe(true); + }); + + it('should reject an invalid string', () => + { + const validator = new Validator(); + const data = { string: 123 }; + const scheme = VALIDATION_SCHEMES.STRING; + + const result = validator.validate(data, scheme); + + expect(result.valid).toBe(false); + expect(result.errors).toContain(VALUES.MESSAGES.INVALID_STRING); + }); + + it('should reject a missing required string', () => + { + const validator = new Validator(); + const data = { string: undefined }; + const scheme = VALIDATION_SCHEMES.STRING; + + const result = validator.validate(data, scheme); + + expect(result.valid).toBe(false); + expect(result.errors).toContain(VALUES.MESSAGES.MISSING_STRING); + }); + }); + + describe('Integer values', () => + { + it('should accept a valid integer', () => + { + const validator = new Validator(); + const data = { integer: 123 }; + const scheme = VALIDATION_SCHEMES.INTEGER; + + const result = validator.validate(data, scheme); + + expect(result.valid).toBe(true); + }); + + it('should reject an invalid integer', () => + { + const validator = new Validator(); + const data = { integer: '123' }; + const scheme = VALIDATION_SCHEMES.INTEGER; + + const result = validator.validate(data, scheme); + + expect(result.valid).toBe(false); + expect(result.errors).toContain(VALUES.MESSAGES.INVALID_INTEGER); + }); + + it('should reject a missing required integer', () => + { + const validator = new Validator(); + const data = { integer: undefined }; + const scheme = VALIDATION_SCHEMES.INTEGER; + + const result = validator.validate(data, scheme); + + expect(result.valid).toBe(false); + expect(result.errors).toContain(VALUES.MESSAGES.MISSING_INTEGER); + }); + }); + + describe('Real values', () => + { + it('should accept a valid real', () => + { + const validator = new Validator(); + const data = { real: 123.45 }; + const scheme = VALIDATION_SCHEMES.REAL; + + const result = validator.validate(data, scheme); + + expect(result.valid).toBe(true); + }); + + it('should reject an invalid real', () => + { + const validator = new Validator(); + const data = { real: '123.45' }; + const scheme = VALIDATION_SCHEMES.REAL; + + const result = validator.validate(data, scheme); + + expect(result.valid).toBe(false); + expect(result.errors).toContain(VALUES.MESSAGES.INVALID_REAL); + }); + + it('should reject a missing required real', () => + { + const validator = new Validator(); + const data = { real: undefined }; + const scheme = VALIDATION_SCHEMES.REAL; + + const result = validator.validate(data, scheme); + + expect(result.valid).toBe(false); + expect(result.errors).toContain(VALUES.MESSAGES.MISSING_REAL); + }); + }); + + describe('Boolean values', () => + { + it('should accept a valid boolean', () => + { + const validator = new Validator(); + const data = { boolean: true }; + const scheme = VALIDATION_SCHEMES.BOOLEAN; + + const result = validator.validate(data, scheme); + + expect(result.valid).toBe(true); + }); + + it('should reject an invalid boolean', () => + { + const validator = new Validator(); + const data = { boolean: 'true' }; + const scheme = VALIDATION_SCHEMES.BOOLEAN; + + const result = validator.validate(data, scheme); + + expect(result.valid).toBe(false); + expect(result.errors).toContain(VALUES.MESSAGES.INVALID_BOOLEAN); + }); + + it('should reject a missing required boolean', () => + { + const validator = new Validator(); + const data = { boolean: undefined }; + const scheme = VALIDATION_SCHEMES.BOOLEAN; + + const result = validator.validate(data, scheme); + + expect(result.valid).toBe(false); + expect(result.errors).toContain(VALUES.MESSAGES.MISSING_BOOLEAN); + }); + }); + + describe('URL values', () => + { + it('should accept a valid URL', () => + { + const validator = new Validator(); + const data = { url: 'https://example.com' }; + const scheme = VALIDATION_SCHEMES.URL; + + const result = validator.validate(data, scheme); + + expect(result.valid).toBe(true); + }); + + it('should reject an invalid URL', () => + { + const validator = new Validator(); + const data = { url: 'example.com' }; + const scheme = VALIDATION_SCHEMES.URL; + + const result = validator.validate(data, scheme); + + expect(result.valid).toBe(false); + expect(result.errors).toContain(VALUES.MESSAGES.INVALID_URL); + }); + + it('should reject a missing required URL', () => + { + const validator = new Validator(); + const data = { url: undefined }; + const scheme = VALIDATION_SCHEMES.URL; + + const result = validator.validate(data, scheme); + + expect(result.valid).toBe(false); + expect(result.errors).toContain(VALUES.MESSAGES.MISSING_URL); + }); + }); + + describe('Optional values', () => + { + it('should accept missing optional values', () => + { + const validator = new Validator(); + const data = { string: undefined, integer: undefined, real: undefined, boolean: undefined, url: undefined }; + const scheme = VALIDATION_SCHEMES.OPTIONAL; + + const result = validator.validate(data, scheme); + + expect(result.valid).toBe(true); + }); + }); + + describe('Group values', () => + { + it('should accept a valid group', () => + { + const validator = new Validator(); + const data = { group: { string: 'source', integer: 123, boolean: true }}; + const scheme = VALIDATION_SCHEMES.GROUP; + + const result = validator.validate(data, scheme); + + expect(result.valid).toBe(true); + }); + + it('should reject an invalid group', () => + { + const validator = new Validator(); + const data = { group: 'group' }; + const scheme = VALIDATION_SCHEMES.GROUP; + + const result = validator.validate(data, scheme); + + expect(result.valid).toBe(false); + expect(result.errors).toContain(VALUES.MESSAGES.INVALID_GROUP); + }); + + it('should reject a missing required group', () => + { + const validator = new Validator(); + const data = { group: undefined }; + const scheme = VALIDATION_SCHEMES.GROUP; + + const result = validator.validate(data, scheme); + + expect(result.valid).toBe(false); + expect(result.errors).toContain(VALUES.MESSAGES.MISSING_GROUP); + }); + }); + + describe('List values', () => + { + it('should accept a valid list', () => + { + const validator = new Validator(); + const data = { list: ['item1', 'item2', 'item3'] }; + const scheme = VALIDATION_SCHEMES.LIST; + + const result = validator.validate(data, scheme); + + expect(result.valid).toBe(true); + }); + + it('should reject an invalid list', () => + { + const validator = new Validator(); + const data = { list: 'item1' }; + const scheme = VALIDATION_SCHEMES.LIST; + + const result = validator.validate(data, scheme); + + expect(result.valid).toBe(false); + expect(result.errors).toContain(VALUES.MESSAGES.INVALID_LIST); + }); + + it('should reject an invalid list item', () => + { + const validator = new Validator(); + const data = { list: ['item1', 123, 'item3'] }; + const scheme = VALIDATION_SCHEMES.LIST; + + const result = validator.validate(data, scheme); + + expect(result.valid).toBe(false); + expect(result.errors).toContain(VALUES.MESSAGES.INVALID_LIST_ITEM); + }); + + it('should reject a missing required list', () => + { + const validator = new Validator(); + const data = { list: undefined }; + const scheme = VALIDATION_SCHEMES.LIST; + + const result = validator.validate(data, scheme); + + expect(result.valid).toBe(false); + expect(result.errors).toContain(VALUES.MESSAGES.MISSING_LIST); + }); + }); + + describe('Complex values', () => + { + it('should accept a valid complex group', () => + { + const validator = new Validator(); + const data = { complex: { source: 'source', integer: 123, boolean: true, list: ['item1', 'item2', 'item3'] }}; + const scheme = VALIDATION_SCHEMES.COMPLEX; + + const result = validator.validate(data, scheme); + + expect(result.valid).toBe(true); + }); + }); + + describe('Strict mode', () => + { + it('should reject extra fields', () => + { + const validator = new Validator(); + const data = { string: 'string', integer: 2, extra: 'extra' }; + const scheme = VALIDATION_SCHEMES.STRICT; + + const result = validator.validate(data, scheme); + + expect(result.valid).toBe(false); + expect(result.errors).toContain(VALUES.MESSAGES.EXTRA_FIELD); + }); + }); + + describe('Lenient mode', () => + { + it('should accept extra fields', () => + { + const validator = new Validator(false); + const data = { string: 'string', extra: 'extra' }; + const scheme = VALIDATION_SCHEMES.LENIENT; + + const result = validator.validate(data, scheme); + + expect(result.valid).toBe(true); + }); + }); +}); diff --git a/packages/validation/test/fixtures/index.ts b/packages/validation/test/fixtures/index.ts new file mode 100644 index 00000000..e617f285 --- /dev/null +++ b/packages/validation/test/fixtures/index.ts @@ -0,0 +1,3 @@ + +export * from './validationSchemes.fixture'; +export * from './values.fixture'; diff --git a/packages/validation/test/fixtures/validationSchemes.fixture.ts b/packages/validation/test/fixtures/validationSchemes.fixture.ts new file mode 100644 index 00000000..dba2ddd4 --- /dev/null +++ b/packages/validation/test/fixtures/validationSchemes.fixture.ts @@ -0,0 +1,79 @@ + +import { ValidationScheme } from '../../src'; + +export const VALIDATION_SCHEMES: Record = +{ + STRING: { + string: { type: 'string', required: true } + }, + + INTEGER: { + integer: { type: 'integer', required: true } + }, + + REAL: { + real: { type: 'real', required: true } + }, + + BOOLEAN: { + boolean: { type: 'boolean', required: true } + }, + + URL: { + url: { type: 'url', required: true } + }, + + OPTIONAL: { + string: { type: 'string', required: false }, + integer: { type: 'integer', required: false }, + real: { type: 'real', required: false }, + boolean: { type: 'boolean', required: false }, + url: { type: 'url', required: false } + }, + + GROUP: { + group: { + type: 'group', + required: true, + fields: { + string: { type: 'string', required: true }, + integer: { type: 'integer', required: true }, + boolean: { type: 'boolean', required: true } + } + } + }, + + LIST: { + list: { + type: 'list', + required: true, + items: { type: 'string', required: true } + } + }, + + COMPLEX: { + complex: { + type: 'group', + required: true, + fields: { + source: { type: 'string', required: true }, + integer: { type: 'integer', required: true }, + boolean: { type: 'boolean', required: true }, + list: { + type: 'list', + required: true, + items: { type: 'string', required: true } + } + } + } + }, + + STRICT: { + string: { type: 'string', required: true }, + integer: { type: 'integer', required: false } + }, + + LENIENT: { + string: { type: 'string', required: true } + } +}; diff --git a/packages/validation/test/fixtures/values.fixture.ts b/packages/validation/test/fixtures/values.fixture.ts new file mode 100644 index 00000000..7d7c98d1 --- /dev/null +++ b/packages/validation/test/fixtures/values.fixture.ts @@ -0,0 +1,23 @@ + +export const VALUES = +{ + MESSAGES: + { + INVALID_STRING: `Field 'string' is not a string`, + MISSING_STRING: `Field 'string' is required`, + INVALID_INTEGER: `Field 'integer' is not an integer`, + MISSING_INTEGER: `Field 'integer' is required`, + INVALID_REAL: `Field 'real' is not a real number`, + MISSING_REAL: `Field 'real' is required`, + INVALID_BOOLEAN: `Field 'boolean' is not a boolean`, + MISSING_BOOLEAN: `Field 'boolean' is required`, + INVALID_URL: `Field 'url' is not a valid URL`, + MISSING_URL: `Field 'url' is required`, + INVALID_GROUP: `Field 'group' is not an object`, + MISSING_GROUP: `Field 'group' is required`, + INVALID_LIST: `Field 'list' is not a list`, + INVALID_LIST_ITEM: `Field 'list.1' is not a string`, + MISSING_LIST: `Field 'list' is required`, + EXTRA_FIELD: `Unknown field 'extra'`, + }, +}; diff --git a/packages/validation/tsconfig.json b/packages/validation/tsconfig.json new file mode 100644 index 00000000..e66fac12 --- /dev/null +++ b/packages/validation/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "rootDir": "./src/", + "moduleResolution": "node", + "declaration": true, + "outDir": "./dist", + "removeComments": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "skipLibCheck": true + }, + "exclude": [ + "vite.config.ts", + "node_modules", + "dist", + "test" + ] +} \ No newline at end of file diff --git a/packages/validation/vite.config.ts b/packages/validation/vite.config.ts new file mode 100644 index 00000000..f5182e1f --- /dev/null +++ b/packages/validation/vite.config.ts @@ -0,0 +1,10 @@ +// vite.config.ts +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + coverage: { + provider: 'v8' + }, + }, +}); diff --git a/test.http b/test.http new file mode 100644 index 00000000..b14adac1 --- /dev/null +++ b/test.http @@ -0,0 +1,8 @@ + +POST http://localhost:3000/workers HTTP/1.1 +Content-Type: application/json + +{ + "url": "127.0.0.1:3001", + "procedureNames": "bbb" +} diff --git a/website/package-lock.json b/website/package-lock.json deleted file mode 100644 index 0888b617..00000000 --- a/website/package-lock.json +++ /dev/null @@ -1,3103 +0,0 @@ -{ - "name": "jitar-website", - "version": "0.1.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "jitar-website", - "version": "0.1.0", - "license": "MIT", - "devDependencies": { - "cpx2": "^7.0.1", - "minify": "^11.4.0", - "npm-run-all": "^4.1.5", - "rimraf": "^5.0.8" - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.0.tgz", - "integrity": "sha512-3sG8Zwa5fMcA9bgqB8AfWPQ+HFke6uD3h1s3RIwUNK8EG7a4buxvuFTs3j1IMs2NXAk9F30C/FF4vxRgQCcmoQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.0.tgz", - "integrity": "sha512-+KuOHTKKyIKgEEqKbGTK8W7mPp+hKinbMBeEnNzjJGyFcWsfrXjSTNluJHCY1RqhxFurdD8uNXQDei7qDlR6+g==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.0.tgz", - "integrity": "sha512-EuHFUYkAVfU4qBdyivULuu03FhJO4IJN9PGuABGrFy4vUuzk91P2d+npxHcFdpUnfYKy0PuV+n6bKIpHOB3prQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.0.tgz", - "integrity": "sha512-WRrmKidLoKDl56LsbBMhzTTBxrsVwTKdNbKDalbEZr0tcsBgCLbEtoNthOW6PX942YiYq8HzEnb4yWQMLQuipQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.0.tgz", - "integrity": "sha512-YLntie/IdS31H54Ogdn+v50NuoWF5BDkEUFpiOChVa9UnKpftgwzZRrI4J132ETIi+D8n6xh9IviFV3eXdxfow==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.0.tgz", - "integrity": "sha512-IMQ6eme4AfznElesHUPDZ+teuGwoRmVuuixu7sv92ZkdQcPbsNHzutd+rAfaBKo8YK3IrBEi9SLLKWJdEvJniQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.0.tgz", - "integrity": "sha512-0muYWCng5vqaxobq6LB3YNtevDFSAZGlgtLoAc81PjUfiFz36n4KMpwhtAd4he8ToSI3TGyuhyx5xmiWNYZFyw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.0.tgz", - "integrity": "sha512-XKDVu8IsD0/q3foBzsXGt/KjD/yTKBCIwOHE1XwiXmrRwrX6Hbnd5Eqn/WvDekddK21tfszBSrE/WMaZh+1buQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.0.tgz", - "integrity": "sha512-SEELSTEtOFu5LPykzA395Mc+54RMg1EUgXP+iw2SJ72+ooMwVsgfuwXo5Fn0wXNgWZsTVHwY2cg4Vi/bOD88qw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.0.tgz", - "integrity": "sha512-j1t5iG8jE7BhonbsEg5d9qOYcVZv/Rv6tghaXM/Ug9xahM0nX/H2gfu6X6z11QRTMT6+aywOMA8TDkhPo8aCGw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.0.tgz", - "integrity": "sha512-P7O5Tkh2NbgIm2R6x1zGJJsnacDzTFcRWZyTTMgFdVit6E98LTxO+v8LCCLWRvPrjdzXHx9FEOA8oAZPyApWUA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.0.tgz", - "integrity": "sha512-InQwepswq6urikQiIC/kkx412fqUZudBO4SYKu0N+tGhXRWUqAx+Q+341tFV6QdBifpjYgUndV1hhMq3WeJi7A==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.0.tgz", - "integrity": "sha512-J9rflLtqdYrxHv2FqXE2i1ELgNjT+JFURt/uDMoPQLcjWQA5wDKgQA4t/dTqGa88ZVECKaD0TctwsUfHbVoi4w==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.0.tgz", - "integrity": "sha512-cShCXtEOVc5GxU0fM+dsFD10qZ5UpcQ8AM22bYj0u/yaAykWnqXJDpd77ublcX6vdDsWLuweeuSNZk4yUxZwtw==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.0.tgz", - "integrity": "sha512-HEtaN7Y5UB4tZPeQmgz/UhzoEyYftbMXrBCUjINGjh3uil+rB/QzzpMshz3cNUxqXN7Vr93zzVtpIDL99t9aRw==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.0.tgz", - "integrity": "sha512-WDi3+NVAuyjg/Wxi+o5KPqRbZY0QhI9TjrEEm+8dmpY9Xir8+HE/HNx2JoLckhKbFopW0RdO2D72w8trZOV+Wg==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.0.tgz", - "integrity": "sha512-a3pMQhUEJkITgAw6e0bWA+F+vFtCciMjW/LPtoj99MhVt+Mfb6bbL9hu2wmTZgNd994qTAEw+U/r6k3qHWWaOQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.0.tgz", - "integrity": "sha512-cRK+YDem7lFTs2Q5nEv/HHc4LnrfBCbH5+JHu6wm2eP+d8OZNoSMYgPZJq78vqQ9g+9+nMuIsAO7skzphRXHyw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.0.tgz", - "integrity": "sha512-suXjq53gERueVWu0OKxzWqk7NxiUWSUlrxoZK7usiF50C6ipColGR5qie2496iKGYNLhDZkPxBI3erbnYkU0rQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.0.tgz", - "integrity": "sha512-6p3nHpby0DM/v15IFKMjAaayFhqnXV52aEmv1whZHX56pdkK+MEaLoQWj+H42ssFarP1PcomVhbsR4pkz09qBg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.0.tgz", - "integrity": "sha512-BFelBGfrBwk6LVrmFzCq1u1dZbG4zy/Kp93w2+y83Q5UGYF1d8sCzeLI9NXjKyujjBBniQa8R8PzLFAUrSM9OA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.0.tgz", - "integrity": "sha512-lY6AC8p4Cnb7xYHuIxQ6iYPe6MfO2CC43XXKo9nBXDb35krYt7KGhQnOkRGar5psxYkircpCqfbNDB4uJbS2jQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.0.tgz", - "integrity": "sha512-7L1bHlOTcO4ByvI7OXVI5pNN6HSu6pUQq9yodga8izeuB1KcT2UkHaH6118QJwopExPn0rMHIseCTx1CRo/uNA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.0.tgz", - "integrity": "sha512-Arm+WgUFLUATuoxCJcahGuk6Yj9Pzxd6l11Zb/2aAuv5kWWvvfhLFo2fni4uSK5vzlUdCGZ/BdV5tH8klj8p8g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "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==", - "dev": true, - "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/@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", - "dev": true, - "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", - "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/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==", - "dev": true, - "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==", - "dev": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.22", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.22.tgz", - "integrity": "sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "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, - "optional": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@putout/minify": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@putout/minify/-/minify-4.3.1.tgz", - "integrity": "sha512-XIkJx93TDReV+zYaCB+CnfASHm26vAOEpBmAKzcW/56/8LxU8TRoetUBelkACUab8GPGHjElp0n6a2cLI+ccnw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/@swc/core": { - "version": "1.6.13", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.6.13.tgz", - "integrity": "sha512-eailUYex6fkfaQTev4Oa3mwn0/e3mQU4H8y1WPuImYQESOQDtVrowwUGDSc19evpBbHpKtwM+hw8nLlhIsF+Tw==", - "dev": true, - "hasInstallScript": true, - "license": "Apache-2.0", - "dependencies": { - "@swc/counter": "^0.1.3", - "@swc/types": "^0.1.9" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/swc" - }, - "optionalDependencies": { - "@swc/core-darwin-arm64": "1.6.13", - "@swc/core-darwin-x64": "1.6.13", - "@swc/core-linux-arm-gnueabihf": "1.6.13", - "@swc/core-linux-arm64-gnu": "1.6.13", - "@swc/core-linux-arm64-musl": "1.6.13", - "@swc/core-linux-x64-gnu": "1.6.13", - "@swc/core-linux-x64-musl": "1.6.13", - "@swc/core-win32-arm64-msvc": "1.6.13", - "@swc/core-win32-ia32-msvc": "1.6.13", - "@swc/core-win32-x64-msvc": "1.6.13" - }, - "peerDependencies": { - "@swc/helpers": "*" - }, - "peerDependenciesMeta": { - "@swc/helpers": { - "optional": true - } - } - }, - "node_modules/@swc/core-darwin-arm64": { - "version": "1.6.13", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.6.13.tgz", - "integrity": "sha512-SOF4buAis72K22BGJ3N8y88mLNfxLNprTuJUpzikyMGrvkuBFNcxYtMhmomO0XHsgLDzOJ+hWzcgjRNzjMsUcQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-darwin-x64": { - "version": "1.6.13", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.6.13.tgz", - "integrity": "sha512-AW8akFSC+tmPE6YQQvK9S2A1B8pjnXEINg+gGgw0KRUUXunvu1/OEOeC5L2Co1wAwhD7bhnaefi06Qi9AiwOag==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.6.13", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.6.13.tgz", - "integrity": "sha512-f4gxxvDXVUm2HLYXRd311mSrmbpQF2MZ4Ja6XCQz1hWAxXdhRl1gpnZ+LH/xIfGSwQChrtLLVrkxdYUCVuIjFg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.6.13", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.6.13.tgz", - "integrity": "sha512-Nf/eoW2CbG8s+9JoLtjl9FByBXyQ5cjdBsA4efO7Zw4p+YSuXDgc8HRPC+E2+ns0praDpKNZtLvDtmF2lL+2Gg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.6.13", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.6.13.tgz", - "integrity": "sha512-2OysYSYtdw79prJYuKIiux/Gj0iaGEbpS2QZWCIY4X9sGoETJ5iMg+lY+YCrIxdkkNYd7OhIbXdYFyGs/w5LDg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-x64-gnu": { - "version": "1.6.13", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.6.13.tgz", - "integrity": "sha512-PkR4CZYJNk5hcd2+tMWBpnisnmYsUzazI1O5X7VkIGFcGePTqJ/bWlfUIVVExWxvAI33PQFzLbzmN5scyIUyGQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-x64-musl": { - "version": "1.6.13", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.6.13.tgz", - "integrity": "sha512-OdsY7wryTxCKwGQcwW9jwWg3cxaHBkTTHi91+5nm7hFPpmZMz1HivJrWAMwVE7iXFw+M4l6ugB/wCvpYrUAAjA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.6.13", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.6.13.tgz", - "integrity": "sha512-ap6uNmYjwk9M/+bFEuWRNl3hq4VqgQ/Lk+ID/F5WGqczNr0L7vEf+pOsRAn0F6EV+o/nyb3ePt8rLhE/wjHpPg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.6.13", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.6.13.tgz", - "integrity": "sha512-IJ8KH4yIUHTnS/U1jwQmtbfQals7zWPG0a9hbEfIr4zI0yKzjd83lmtS09lm2Q24QBWOCFGEEbuZxR4tIlvfzA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.6.13", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.6.13.tgz", - "integrity": "sha512-f6/sx6LMuEnbuxtiSL/EkR0Y6qUHFw1XVrh6rwzKXptTipUdOY+nXpKoh+1UsBm/r7H0/5DtOdrn3q5ZHbFZjQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/counter": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", - "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/@swc/types": { - "version": "0.1.9", - "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.9.tgz", - "integrity": "sha512-qKnCno++jzcJ4lM4NTfYifm1EFSCeIfKiAHAfkENZAV5Kl9PjJIyd2yeeVv6c/2CckuLyv2NmRC5pv6pm2WQBg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@swc/counter": "^0.1.3" - } - }, - "node_modules/acorn": { - "version": "8.11.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", - "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "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==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/array-buffer-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", - "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "is-array-buffer": "^3.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz", - "integrity": "sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==", - "dev": true, - "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", - "is-array-buffer": "^3.0.2", - "is-shared-array-buffer": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, - "node_modules/call-bind": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", - "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.1", - "set-function-length": "^1.1.1" - }, - "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==", - "dev": true, - "dependencies": { - "pascal-case": "^3.1.2", - "tslib": "^2.0.3" - } - }, - "node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/clean-css": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz", - "integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==", - "dev": true, - "dependencies": { - "source-map": "~0.6.0" - }, - "engines": { - "node": ">= 10.0" - } - }, - "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/commander": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", - "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", - "dev": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "node_modules/cpx2": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/cpx2/-/cpx2-7.0.1.tgz", - "integrity": "sha512-ZgK/DRvPFM5ATZ5DQ5UzY6ajkBrI/p9Uc7VyLHc7b4OSFeBO4yOQz/GEmccc4Om6capGYlY4K1XX+BtYQiPPIA==", - "dev": true, - "dependencies": { - "debounce": "^2.0.0", - "debug": "^4.1.1", - "duplexer": "^0.1.1", - "fs-extra": "^11.1.0", - "glob": "^10.3.10", - "glob2base": "0.0.12", - "ignore": "^5.2.4", - "minimatch": "^9.0.0", - "p-map": "^6.0.0", - "resolve": "^1.12.0", - "safe-buffer": "^5.2.0", - "shell-quote": "^1.8.0", - "subarg": "^1.0.0" - }, - "bin": { - "cpx": "bin/index.js" - }, - "engines": { - "node": ">=18", - "npm": ">=10" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/css-b64-images": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/css-b64-images/-/css-b64-images-0.2.5.tgz", - "integrity": "sha512-TgQBEdP07adhrDfXvI5o6bHGukKBNMzp2Ngckc/6d09zpjD2gc1Hl3Ca1CKgb8FXjHi88+Phv2Uegs2kTL4zjg==", - "dev": true, - "bin": { - "css-b64-images": "bin/css-b64-images" - }, - "engines": { - "node": "*" - } - }, - "node_modules/debounce": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/debounce/-/debounce-2.0.0.tgz", - "integrity": "sha512-xRetU6gL1VJbs85Mc4FoEGSjQxzpdxRyFhe3lmWFyy2EzydIcD4xzUvRJMD+NPDfMwKNhxa3PvsIOU32luIWeA==", - "dev": true, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/define-data-property": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", - "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.2.1", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "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/dot-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", - "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", - "dev": true, - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/duplexer": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", - "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", - "dev": true - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true - }, - "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==", - "dev": true - }, - "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "dev": true, - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/es-abstract": { - "version": "1.22.3", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz", - "integrity": "sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==", - "dev": true, - "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "arraybuffer.prototype.slice": "^1.0.2", - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.5", - "es-set-tostringtag": "^2.0.1", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.6", - "get-intrinsic": "^1.2.2", - "get-symbol-description": "^1.0.0", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0", - "internal-slot": "^1.0.5", - "is-array-buffer": "^3.0.2", - "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.2", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.12", - "is-weakref": "^1.0.2", - "object-inspect": "^1.13.1", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.5.1", - "safe-array-concat": "^1.0.1", - "safe-regex-test": "^1.0.0", - "string.prototype.trim": "^1.2.8", - "string.prototype.trimend": "^1.0.7", - "string.prototype.trimstart": "^1.0.7", - "typed-array-buffer": "^1.0.0", - "typed-array-byte-length": "^1.0.0", - "typed-array-byte-offset": "^1.0.0", - "typed-array-length": "^1.0.4", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz", - "integrity": "sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.2.2", - "has-tostringtag": "^1.0.0", - "hasown": "^2.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "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.23.0", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.0.tgz", - "integrity": "sha512-1lvV17H2bMYda/WaFb2jLPeHU3zml2k4/yagNMG8Q/YtfMjCwEUZa2eXXMgZTVSL5q1n4H7sQ0X6CdJDqqeCFA==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.23.0", - "@esbuild/android-arm": "0.23.0", - "@esbuild/android-arm64": "0.23.0", - "@esbuild/android-x64": "0.23.0", - "@esbuild/darwin-arm64": "0.23.0", - "@esbuild/darwin-x64": "0.23.0", - "@esbuild/freebsd-arm64": "0.23.0", - "@esbuild/freebsd-x64": "0.23.0", - "@esbuild/linux-arm": "0.23.0", - "@esbuild/linux-arm64": "0.23.0", - "@esbuild/linux-ia32": "0.23.0", - "@esbuild/linux-loong64": "0.23.0", - "@esbuild/linux-mips64el": "0.23.0", - "@esbuild/linux-ppc64": "0.23.0", - "@esbuild/linux-riscv64": "0.23.0", - "@esbuild/linux-s390x": "0.23.0", - "@esbuild/linux-x64": "0.23.0", - "@esbuild/netbsd-x64": "0.23.0", - "@esbuild/openbsd-arm64": "0.23.0", - "@esbuild/openbsd-x64": "0.23.0", - "@esbuild/sunos-x64": "0.23.0", - "@esbuild/win32-arm64": "0.23.0", - "@esbuild/win32-ia32": "0.23.0", - "@esbuild/win32-x64": "0.23.0" - } - }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/find-index": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/find-index/-/find-index-0.1.1.tgz", - "integrity": "sha512-uJ5vWrfBKMcE6y2Z8834dwEZj9mNGxYa3t3I53OwFeuZ8D9oc2E5zcsrkuhX6h4iYrjhiv0T3szQmxlAV9uxDg==", - "dev": true - }, - "node_modules/find-up": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-7.0.0.tgz", - "integrity": "sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==", - "dev": true, - "dependencies": { - "locate-path": "^7.2.0", - "path-exists": "^5.0.0", - "unicorn-magic": "^0.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "dev": true, - "dependencies": { - "is-callable": "^1.1.3" - } - }, - "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==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.0", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/fs-extra": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", - "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=14.14" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, - "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/get-intrinsic": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", - "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/glob": { - "version": "10.3.10", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", - "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", - "dev": true, - "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/glob2base": { - "version": "0.0.12", - "resolved": "https://registry.npmjs.org/glob2base/-/glob2base-0.0.12.tgz", - "integrity": "sha512-ZyqlgowMbfj2NPjxaZZ/EtsXlOch28FRXgMd64vqZWk1bT9+wvSRLYD1om9M7QfQru51zJPAT17qXm4/zd+9QA==", - "dev": true, - "dependencies": { - "find-index": "^0.1.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "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/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.3" - }, - "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==", - "dev": true - }, - "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": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", - "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.2.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", - "dev": true, - "engines": { - "node": ">= 0.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==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "dev": true, - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", - "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true - }, - "node_modules/html-minifier-terser": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-7.2.0.tgz", - "integrity": "sha512-tXgn3QfqPIpGl9o+K5tpcj3/MN4SfLtsx2GWwBC3SSd0tXQGyF3gsSqad8loJgKZGM3ZxbYDd5yhiBIdWpmvLA==", - "dev": true, - "dependencies": { - "camel-case": "^4.1.2", - "clean-css": "~5.3.2", - "commander": "^10.0.0", - "entities": "^4.4.0", - "param-case": "^3.0.4", - "relateurl": "^0.2.7", - "terser": "^5.15.1" - }, - "bin": { - "html-minifier-terser": "cli.js" - }, - "engines": { - "node": "^14.13.1 || >=16.0.0" - } - }, - "node_modules/ignore": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", - "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/internal-slot": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.6.tgz", - "integrity": "sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.2.2", - "hasown": "^2.0.0", - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-array-buffer": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", - "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.0", - "is-typed-array": "^1.1.10" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true - }, - "node_modules/is-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-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-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "dev": true, - "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==", - "dev": true, - "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-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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-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-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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.12", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", - "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", - "dev": true, - "dependencies": { - "which-typed-array": "^1.1.11" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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/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/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "node_modules/jackspeak": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", - "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", - "dev": true, - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/jju": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", - "integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==", - "dev": true - }, - "node_modules/json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true - }, - "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/locate-path": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", - "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", - "dev": true, - "dependencies": { - "p-locate": "^6.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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==", - "dev": true, - "dependencies": { - "tslib": "^2.0.3" - } - }, - "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==", - "dev": true, - "engines": { - "node": "14 || >=16.14" - } - }, - "node_modules/memorystream": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", - "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", - "dev": true, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/minify": { - "version": "11.4.0", - "resolved": "https://registry.npmjs.org/minify/-/minify-11.4.0.tgz", - "integrity": "sha512-JG0lzxYcNi+UdcqcYDXzllb5Q2GEIaOYZ2yxwX4Y0QE5ffupP4TldbZkVTSFqUwXWhmkaS/vMmZHxKQR+HzWEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@putout/minify": "^4.0.0", - "@swc/core": "^1.6.7", - "clean-css": "^5.0.1", - "css-b64-images": "~0.2.5", - "debug": "^4.1.0", - "esbuild": "^0.23.0", - "find-up": "^7.0.0", - "html-minifier-terser": "^7.1.0", - "readjson": "^2.2.2", - "simport": "^1.2.0", - "terser": "^5.28.1", - "try-catch": "^3.0.0", - "try-to-catch": "^3.0.0" - }, - "bin": { - "minify": "bin/minify.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=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==", - "dev": true, - "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==", - "dev": true, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true - }, - "node_modules/no-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", - "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", - "dev": true, - "dependencies": { - "lower-case": "^2.0.2", - "tslib": "^2.0.3" - } - }, - "node_modules/normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "node_modules/npm-run-all": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", - "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "chalk": "^2.4.1", - "cross-spawn": "^6.0.5", - "memorystream": "^0.3.1", - "minimatch": "^3.0.4", - "pidtree": "^0.3.0", - "read-pkg": "^3.0.0", - "shell-quote": "^1.6.1", - "string.prototype.padend": "^3.0.0" - }, - "bin": { - "npm-run-all": "bin/npm-run-all/index.js", - "run-p": "bin/run-p/index.js", - "run-s": "bin/run-s/index.js" - }, - "engines": { - "node": ">= 4" - } - }, - "node_modules/npm-run-all/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/npm-run-all/node_modules/cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "dependencies": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - }, - "engines": { - "node": ">=4.8" - } - }, - "node_modules/npm-run-all/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/npm-run-all/node_modules/path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/npm-run-all/node_modules/shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", - "dev": true, - "dependencies": { - "shebang-regex": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm-run-all/node_modules/shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm-run-all/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, - "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/p-limit": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", - "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", - "dev": true, - "dependencies": { - "yocto-queue": "^1.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", - "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", - "dev": true, - "dependencies": { - "p-limit": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-map": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-6.0.0.tgz", - "integrity": "sha512-T8BatKGY+k5rU+Q/GTYgrEf2r4xRMevAN5mtXc2aPc4rS1j3s+vWTaO2Wag94neXuCAUAs8cxBL9EeB5EA6diw==", - "dev": true, - "engines": { - "node": ">=16" - }, - "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==", - "dev": true, - "dependencies": { - "dot-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", - "dev": true, - "dependencies": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "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==", - "dev": true, - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/path-exists": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", - "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", - "dev": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "node_modules/path-scurry": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", - "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", - "dev": true, - "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-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "dependencies": { - "pify": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/pidtree": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", - "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==", - "dev": true, - "bin": { - "pidtree": "bin/pidtree.js" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", - "dev": true, - "dependencies": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/readjson": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/readjson/-/readjson-2.2.2.tgz", - "integrity": "sha512-PdeC9tsmLWBiL8vMhJvocq+OezQ3HhsH2HrN7YkhfYcTjQSa/iraB15A7Qvt7Xpr0Yd2rDNt6GbFwVQDg3HcAw==", - "dev": true, - "dependencies": { - "jju": "^1.4.0", - "try-catch": "^3.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/regexp.prototype.flags": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", - "integrity": "sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "set-function-name": "^2.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/relateurl": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", - "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", - "dev": true, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dev": true, - "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/rimraf": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.8.tgz", - "integrity": "sha512-XSh0V2/yNhDEi8HwdIefD8MLgs4LQXPag/nEJWs3YUc3Upn+UHa1GyIkEg9xSSNt7HnkO5FjTvmcRzgf+8UZuw==", - "dev": true, - "license": "ISC", - "dependencies": { - "glob": "^10.3.7" - }, - "bin": { - "rimraf": "dist/esm/bin.mjs" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "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==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/safe-regex-test": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.2.tgz", - "integrity": "sha512-83S9w6eFq12BBIJYvjMux6/dkirb8+4zJRA9cxNBVb7Wq5fJBW+Xze48WqR8pxua7bDuAaaAxtVVd4Idjp1dBQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.5", - "get-intrinsic": "^1.2.2", - "is-regex": "^1.1.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/set-function-length": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.0.tgz", - "integrity": "sha512-4DBHDoyHlM1IRPGYcoxexgh67y4ueR53FKV1yyxwFMY7aCqcN/38M1+SwZ/qJQ8iLv7+ck385ot4CcisOAPT9w==", - "dev": true, - "dependencies": { - "define-data-property": "^1.1.1", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.2", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/set-function-name": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", - "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==", - "dev": true, - "dependencies": { - "define-data-property": "^1.0.1", - "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/shell-quote": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", - "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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==", - "dev": true, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/simport": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/simport/-/simport-1.2.0.tgz", - "integrity": "sha512-85Bm7pKsqiiQ8rmYCaPDdlXZjJvuW6/k/FY8MTtLFMgU7f8S00CgTHfRtWB6KwSb6ek4p9YyG2enG1+yJbl+CA==", - "dev": true, - "dependencies": { - "readjson": "^2.2.0", - "try-to-catch": "^3.0.0" - }, - "engines": { - "node": ">=12.2" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-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==", - "dev": true, - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/spdx-correct": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", - "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", - "dev": true, - "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-exceptions": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.4.0.tgz", - "integrity": "sha512-hcjppoJ68fhxA/cjbN4T8N6uCUejN8yFw69ttpqtBeCbF3u13n7mb31NB9jKwGTTWWnt9IbRA/mf1FprYS8wfw==", - "dev": true - }, - "node_modules/spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-license-ids": { - "version": "3.0.16", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.16.tgz", - "integrity": "sha512-eWN+LnM3GR6gPu35WxNgbGl8rmY1AEmoMDvL/QD6zYmPWgywxWqJWNdLGT+ke8dKNWrcYgYjPpG5gbTfghP8rw==", - "dev": true - }, - "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==", - "dev": true, - "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==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/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==", - "dev": true, - "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==", - "dev": true - }, - "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==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string.prototype.padend": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.5.tgz", - "integrity": "sha512-DOB27b/2UTTD+4myKUFh+/fXWcu/UDyASIXfg+7VzoCNNGOfWvoyU/x5pvVHr++ztyt/oSYI1BcWBBG/hmlNjA==", - "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.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": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "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==", - "dev": true, - "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==", - "dev": true, - "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/subarg": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/subarg/-/subarg-1.0.0.tgz", - "integrity": "sha512-RIrIdRY0X1xojthNcVtgT9sjpOGagEUKpZdgBUi054OEPFo282yg+zE+t1Rj3+RqKq2xStL7uUHhY+AjbC4BXg==", - "dev": true, - "dependencies": { - "minimist": "^1.1.0" - } - }, - "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/terser": { - "version": "5.31.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.31.1.tgz", - "integrity": "sha512-37upzU1+viGvuFtBo9NPufCb9dwM0+l9hMxYyWfBA+fbwrPqNJAhbZ6W47bBFnZHKHTUBnMvi87434qq+qnxOg==", - "dev": true, - "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/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - }, - "node_modules/try-catch": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/try-catch/-/try-catch-3.0.1.tgz", - "integrity": "sha512-91yfXw1rr/P6oLpHSyHDOHm0vloVvUoo9FVdw8YwY05QjJQG9OT0LUxe2VRAzmHG+0CUOmI3nhxDUMLxDN/NEQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/try-to-catch": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/try-to-catch/-/try-to-catch-3.0.1.tgz", - "integrity": "sha512-hOY83V84Hx/1sCzDSaJA+Xz2IIQOHRvjxzt+F0OjbQGPZ6yLPLArMA0gw/484MlfUkQbCpKYMLX3VDCAjWKfzQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true - }, - "node_modules/typed-array-buffer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", - "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1", - "is-typed-array": "^1.1.10" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/typed-array-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", - "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "has-proto": "^1.0.1", - "is-typed-array": "^1.1.10" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-byte-offset": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", - "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", - "dev": true, - "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "has-proto": "^1.0.1", - "is-typed-array": "^1.1.10" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-length": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", - "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "is-typed-array": "^1.1.9" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/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/unicorn-magic": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", - "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", - "dev": true, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/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.13", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.13.tgz", - "integrity": "sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==", - "dev": true, - "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.4", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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==", - "dev": true, - "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==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-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==", - "dev": true, - "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==", - "dev": true, - "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/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/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==", - "dev": true - }, - "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==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/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==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "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==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "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" - } - } - } -} diff --git a/website/package.json b/website/package.json index f4b99cdc..b8ca297f 100644 --- a/website/package.json +++ b/website/package.json @@ -5,17 +5,15 @@ "author": "Masking Technology", "license": "MIT", "scripts": { - "build": "npm-run-all clean copy minify", - "clean": "rimraf dist", + "build": "npm run clean && npm run copy && npm run minify", + "clean": "rm -rf dist", "copy": "cpx -u 'src/**/*.{svg,png, txt}' dist", - "minify": "npm-run-all minify-html minify-css", + "minify": "npm run minify-html && npm run minify-css", "minify-html": "minify src/index.html > dist/index.html", "minify-css": "minify src/css/*.css src/css/**/*.css src/fonts/**/*.css > dist/site.css" }, "devDependencies": { "cpx2": "^7.0.1", - "minify": "^11.4.0", - "npm-run-all": "^4.1.5", - "rimraf": "^5.0.8" + "minify": "^11.4.1" } } \ No newline at end of file