diff --git a/packages/data-sources/duckdb/.gitignore b/packages/core/.gitignore similarity index 100% rename from packages/data-sources/duckdb/.gitignore rename to packages/core/.gitignore diff --git a/packages/core/docs/01-faqs.md b/packages/core/docs/01-faqs.md deleted file mode 100644 index 1d2ccbf..0000000 --- a/packages/core/docs/01-faqs.md +++ /dev/null @@ -1,37 +0,0 @@ -# Frequently Asked Questions - -> Why another (JavaScript) framework? - -Yes. - -> No, but seriously? Why JavaScript? - -I find it comfortable to create stuff in TypeScript. However, the reader must pay attention to the underlying -architecture instead of the specifics of the platform being implemented on. - -> Then why another framework? - -Most frameworks focus on the routing and the logic side of things. This is reasonable for most applications. However, -the benefits of hypermedia becomes an afterthought, wherein attempts to produce rich structured data become ignored -(as compliance with Web standards is not a requirement for building Web services, nevertheless some developers comply -with select aspects of REST in order to create some rudimentary RPC service). - -> What's the point then? - -The goal of the framework is to empower developers on focusing on the business logic while not being hindered by -considering proper REST (and ultimately HATEOAS) guidelines on the resulting implementation. - -> Why didn't you extend $FRAMEWORK instead? - -Frameworks tend to include a lot of built-in plugins/code. I opted on using a minimal setup for a truly compositional -API (add any languages/media types/request handlers/request decorators etc.), however defaults are provided sensibly -(default response is serialized as JSON because it's already a ubiquitous media type). - -> What is the expected output of this project? - -The choices include implementing a plugin to complement popular frameworks, as well as an entirely new framework with -adapters to various common components of Web services (such as persistent storage). - -> What's the timeline for this project? - -Indefinite. However, I shall provide milestones if necessary for certain periods of time. diff --git a/packages/core/docs/bruno/Create Resource.bru b/packages/core/docs/bruno/Create Resource.bru deleted file mode 100644 index e5a7b5a..0000000 --- a/packages/core/docs/bruno/Create Resource.bru +++ /dev/null @@ -1,26 +0,0 @@ -meta { - name: Create Resource - type: http - seq: 5 -} - -post { - url: http://localhost:3000/api/users - body: json - auth: none -} - -headers { - Content-Type: application/json -} - -body:json { - { - "firstName": "John", - "middleName": "Smith", - "lastName": "Doe", - "birthday": "1986-04-20", - "bio": "This is my profile!" - } - -} diff --git a/packages/core/docs/bruno/Delete Resource.bru b/packages/core/docs/bruno/Delete Resource.bru deleted file mode 100644 index b30cd7d..0000000 --- a/packages/core/docs/bruno/Delete Resource.bru +++ /dev/null @@ -1,11 +0,0 @@ -meta { - name: Delete Resource - type: http - seq: 8 -} - -delete { - url: http://localhost:3000/api/users/2 - body: none - auth: none -} diff --git a/packages/core/docs/bruno/Emplace Resource.bru b/packages/core/docs/bruno/Emplace Resource.bru deleted file mode 100644 index 7264acb..0000000 --- a/packages/core/docs/bruno/Emplace Resource.bru +++ /dev/null @@ -1,23 +0,0 @@ -meta { - name: Emplace Resource - type: http - seq: 6 -} - -put { - url: http://localhost:3000/api/users/2 - body: json - auth: none -} - -body:json { - { - "firstName": "John", - "middleName": "Smith", - "lastName": "Doe", - "birthday": "1986-04-20", - "bio": "This is my profile!", - "id": 2 - } - -} diff --git a/packages/core/docs/bruno/Get Resource Collection Metadata.bru b/packages/core/docs/bruno/Get Resource Collection Metadata.bru deleted file mode 100644 index 45b8553..0000000 --- a/packages/core/docs/bruno/Get Resource Collection Metadata.bru +++ /dev/null @@ -1,11 +0,0 @@ -meta { - name: Get Resource Collection Metadata - type: http - seq: 1 -} - -head { - url: http://localhost:3000/api/users - body: none - auth: none -} diff --git a/packages/core/docs/bruno/Get Resource Collection.bru b/packages/core/docs/bruno/Get Resource Collection.bru deleted file mode 100644 index 270804e..0000000 --- a/packages/core/docs/bruno/Get Resource Collection.bru +++ /dev/null @@ -1,11 +0,0 @@ -meta { - name: Get Resource Collection - type: http - seq: 3 -} - -get { - url: http://localhost:3000/api/users - body: none - auth: none -} diff --git a/packages/core/docs/bruno/Get Resource Metadata.bru b/packages/core/docs/bruno/Get Resource Metadata.bru deleted file mode 100644 index d798caa..0000000 --- a/packages/core/docs/bruno/Get Resource Metadata.bru +++ /dev/null @@ -1,11 +0,0 @@ -meta { - name: Get Resource Metadata - type: http - seq: 2 -} - -head { - url: http://localhost:3000/api/users/1 - body: none - auth: none -} diff --git a/packages/core/docs/bruno/Get Resource.bru b/packages/core/docs/bruno/Get Resource.bru deleted file mode 100644 index a8fa8b4..0000000 --- a/packages/core/docs/bruno/Get Resource.bru +++ /dev/null @@ -1,11 +0,0 @@ -meta { - name: Get Resource - type: http - seq: 4 -} - -get { - url: http://localhost:3000/api/users/1 - body: none - auth: none -} diff --git a/packages/core/docs/bruno/Patch Resource.bru b/packages/core/docs/bruno/Patch Resource.bru deleted file mode 100644 index 150a0a4..0000000 --- a/packages/core/docs/bruno/Patch Resource.bru +++ /dev/null @@ -1,22 +0,0 @@ -meta { - name: Patch Resource - type: http - seq: 7 -} - -patch { - url: http://localhost:3000/api/users/1 - body: json - auth: none -} - -headers { - Content-Type: application/json -} - -body:json { - { - "bio": "This is my updated bio!" - } - -} diff --git a/packages/core/docs/bruno/bruno.json b/packages/core/docs/bruno/bruno.json deleted file mode 100644 index 9d7ecaa..0000000 --- a/packages/core/docs/bruno/bruno.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "version": "1", - "name": "Yasumi", - "type": "collection", - "ignore": [ - "node_modules", - ".git" - ], - "presets": { - "requestType": "http", - "requestUrl": "http://localhost:3000" - } -} \ No newline at end of file diff --git a/packages/core/package.json b/packages/core/package.json index ab16474..bab0cf9 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,5 +1,5 @@ { - "name": "@modal-sh/yasumi", + "name": "core", "version": "0.0.0", "files": [ "dist", @@ -13,11 +13,11 @@ "pridepack" ], "devDependencies": { - "@types/node": "^20.11.30", + "@types/node": "^20.11.0", "pridepack": "2.6.0", "tslib": "^2.6.2", - "typescript": "^5.4.3", - "vitest": "^1.4.0" + "typescript": "^5.3.3", + "vitest": "^1.2.0" }, "scripts": { "prepublishOnly": "pridepack clean && pridepack build", @@ -30,7 +30,7 @@ "test": "vitest" }, "private": false, - "description": "HATEOAS-first backend framework", + "description": "Core module for Yasumi.", "repository": { "url": "", "type": "git" @@ -44,49 +44,6 @@ "access": "public" }, "dependencies": { - "tsx": "^4.7.1", "valibot": "^0.30.0" - }, - "types": "./dist/types/common/index.d.ts", - "main": "./dist/cjs/production/index.js", - "module": "./dist/esm/production/index.js", - "exports": { - ".": { - "development": { - "require": "./dist/cjs/development/index.js", - "import": "./dist/esm/development/index.js" - }, - "require": "./dist/cjs/production/index.js", - "import": "./dist/esm/production/index.js", - "types": "./dist/types/common/index.d.ts" - }, - "./backend": { - "development": { - "require": "./dist/cjs/development/backend.js", - "import": "./dist/esm/development/backend.js" - }, - "require": "./dist/cjs/production/backend.js", - "import": "./dist/esm/production/backend.js", - "types": "./dist/types/backend/index.d.ts" - }, - "./client": { - "development": { - "require": "./dist/cjs/development/client.js", - "import": "./dist/esm/development/client.js" - }, - "require": "./dist/cjs/production/client.js", - "import": "./dist/esm/production/client.js", - "types": "./dist/types/client/index.d.ts" - } - }, - "typesVersions": { - "*": { - "backend": [ - "./dist/types/backend/index.d.ts" - ], - "client": [ - "./dist/types/client/index.d.ts" - ] - } } } diff --git a/packages/core/pnpm-lock.yaml b/packages/core/pnpm-lock.yaml deleted file mode 100644 index 460d7a2..0000000 --- a/packages/core/pnpm-lock.yaml +++ /dev/null @@ -1,1514 +0,0 @@ -lockfileVersion: '6.0' - -settings: - autoInstallPeers: true - excludeLinksFromLockfile: false - -dependencies: - negotiator: - specifier: ^0.6.3 - version: 0.6.3 - tsx: - specifier: ^4.7.1 - version: 4.7.1 - valibot: - specifier: ^0.30.0 - version: 0.30.0 - -devDependencies: - '@types/negotiator': - specifier: ^0.6.3 - version: 0.6.3 - '@types/node': - specifier: ^20.11.30 - version: 20.11.30 - pridepack: - specifier: 2.6.0 - version: 2.6.0(tslib@2.6.2)(typescript@5.4.3) - tslib: - specifier: ^2.6.2 - version: 2.6.2 - typescript: - specifier: ^5.4.3 - version: 5.4.3 - vitest: - specifier: ^1.4.0 - version: 1.4.0(@types/node@20.11.30) - -packages: - - /@esbuild/aix-ppc64@0.19.12: - resolution: {integrity: sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [aix] - requiresBuild: true - optional: true - - /@esbuild/android-arm64@0.19.12: - resolution: {integrity: sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==} - engines: {node: '>=12'} - cpu: [arm64] - os: [android] - requiresBuild: true - optional: true - - /@esbuild/android-arm@0.19.12: - resolution: {integrity: sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==} - engines: {node: '>=12'} - cpu: [arm] - os: [android] - requiresBuild: true - optional: true - - /@esbuild/android-x64@0.19.12: - resolution: {integrity: sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==} - engines: {node: '>=12'} - cpu: [x64] - os: [android] - requiresBuild: true - optional: true - - /@esbuild/darwin-arm64@0.19.12: - resolution: {integrity: sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==} - engines: {node: '>=12'} - cpu: [arm64] - os: [darwin] - requiresBuild: true - optional: true - - /@esbuild/darwin-x64@0.19.12: - resolution: {integrity: sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==} - engines: {node: '>=12'} - cpu: [x64] - os: [darwin] - requiresBuild: true - optional: true - - /@esbuild/freebsd-arm64@0.19.12: - resolution: {integrity: sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==} - engines: {node: '>=12'} - cpu: [arm64] - os: [freebsd] - requiresBuild: true - optional: true - - /@esbuild/freebsd-x64@0.19.12: - resolution: {integrity: sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==} - engines: {node: '>=12'} - cpu: [x64] - os: [freebsd] - requiresBuild: true - optional: true - - /@esbuild/linux-arm64@0.19.12: - resolution: {integrity: sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==} - engines: {node: '>=12'} - cpu: [arm64] - os: [linux] - requiresBuild: true - optional: true - - /@esbuild/linux-arm@0.19.12: - resolution: {integrity: sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==} - engines: {node: '>=12'} - cpu: [arm] - os: [linux] - requiresBuild: true - optional: true - - /@esbuild/linux-ia32@0.19.12: - resolution: {integrity: sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==} - engines: {node: '>=12'} - cpu: [ia32] - os: [linux] - requiresBuild: true - optional: true - - /@esbuild/linux-loong64@0.19.12: - resolution: {integrity: sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==} - engines: {node: '>=12'} - cpu: [loong64] - os: [linux] - requiresBuild: true - optional: true - - /@esbuild/linux-mips64el@0.19.12: - resolution: {integrity: sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==} - engines: {node: '>=12'} - cpu: [mips64el] - os: [linux] - requiresBuild: true - optional: true - - /@esbuild/linux-ppc64@0.19.12: - resolution: {integrity: sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [linux] - requiresBuild: true - optional: true - - /@esbuild/linux-riscv64@0.19.12: - resolution: {integrity: sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==} - engines: {node: '>=12'} - cpu: [riscv64] - os: [linux] - requiresBuild: true - optional: true - - /@esbuild/linux-s390x@0.19.12: - resolution: {integrity: sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==} - engines: {node: '>=12'} - cpu: [s390x] - os: [linux] - requiresBuild: true - optional: true - - /@esbuild/linux-x64@0.19.12: - resolution: {integrity: sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==} - engines: {node: '>=12'} - cpu: [x64] - os: [linux] - requiresBuild: true - optional: true - - /@esbuild/netbsd-x64@0.19.12: - resolution: {integrity: sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==} - engines: {node: '>=12'} - cpu: [x64] - os: [netbsd] - requiresBuild: true - optional: true - - /@esbuild/openbsd-x64@0.19.12: - resolution: {integrity: sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==} - engines: {node: '>=12'} - cpu: [x64] - os: [openbsd] - requiresBuild: true - optional: true - - /@esbuild/sunos-x64@0.19.12: - resolution: {integrity: sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==} - engines: {node: '>=12'} - cpu: [x64] - os: [sunos] - requiresBuild: true - optional: true - - /@esbuild/win32-arm64@0.19.12: - resolution: {integrity: sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==} - engines: {node: '>=12'} - cpu: [arm64] - os: [win32] - requiresBuild: true - optional: true - - /@esbuild/win32-ia32@0.19.12: - resolution: {integrity: sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==} - engines: {node: '>=12'} - cpu: [ia32] - os: [win32] - requiresBuild: true - optional: true - - /@esbuild/win32-x64@0.19.12: - resolution: {integrity: sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==} - engines: {node: '>=12'} - cpu: [x64] - os: [win32] - requiresBuild: true - optional: true - - /@jest/schemas@29.6.3: - resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@sinclair/typebox': 0.27.8 - dev: true - - /@jridgewell/sourcemap-codec@1.4.15: - resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} - dev: true - - /@ovyerus/licenses@6.4.4: - resolution: {integrity: sha512-IHjc31WXciQT3hfvdY+M59jBkQp70Fpr04tNDVO5rez2PNv4u8tE6w//CkU+GeBoO9k2ahneSqzjzvlgjyjkGw==} - engines: {node: '>=8'} - dev: true - - /@rollup/rollup-android-arm-eabi@4.12.1: - resolution: {integrity: sha512-iU2Sya8hNn1LhsYyf0N+L4Gf9Qc+9eBTJJJsaOGUp+7x4n2M9dxTt8UvhJl3oeftSjblSlpCfvjA/IfP3g5VjQ==} - cpu: [arm] - os: [android] - requiresBuild: true - dev: true - optional: true - - /@rollup/rollup-android-arm64@4.12.1: - resolution: {integrity: sha512-wlzcWiH2Ir7rdMELxFE5vuM7D6TsOcJ2Yw0c3vaBR3VOsJFVTx9xvwnAvhgU5Ii8Gd6+I11qNHwndDscIm0HXg==} - cpu: [arm64] - os: [android] - requiresBuild: true - dev: true - optional: true - - /@rollup/rollup-darwin-arm64@4.12.1: - resolution: {integrity: sha512-YRXa1+aZIFN5BaImK+84B3uNK8C6+ynKLPgvn29X9s0LTVCByp54TB7tdSMHDR7GTV39bz1lOmlLDuedgTwwHg==} - cpu: [arm64] - os: [darwin] - requiresBuild: true - dev: true - optional: true - - /@rollup/rollup-darwin-x64@4.12.1: - resolution: {integrity: sha512-opjWJ4MevxeA8FhlngQWPBOvVWYNPFkq6/25rGgG+KOy0r8clYwL1CFd+PGwRqqMFVQ4/Qd3sQu5t7ucP7C/Uw==} - cpu: [x64] - os: [darwin] - requiresBuild: true - dev: true - optional: true - - /@rollup/rollup-linux-arm-gnueabihf@4.12.1: - resolution: {integrity: sha512-uBkwaI+gBUlIe+EfbNnY5xNyXuhZbDSx2nzzW8tRMjUmpScd6lCQYKY2V9BATHtv5Ef2OBq6SChEP8h+/cxifQ==} - cpu: [arm] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@rollup/rollup-linux-arm64-gnu@4.12.1: - resolution: {integrity: sha512-0bK9aG1kIg0Su7OcFTlexkVeNZ5IzEsnz1ept87a0TUgZ6HplSgkJAnFpEVRW7GRcikT4GlPV0pbtVedOaXHQQ==} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@rollup/rollup-linux-arm64-musl@4.12.1: - resolution: {integrity: sha512-qB6AFRXuP8bdkBI4D7UPUbE7OQf7u5OL+R94JE42Z2Qjmyj74FtDdLGeriRyBDhm4rQSvqAGCGC01b8Fu2LthQ==} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@rollup/rollup-linux-riscv64-gnu@4.12.1: - resolution: {integrity: sha512-sHig3LaGlpNgDj5o8uPEoGs98RII8HpNIqFtAI8/pYABO8i0nb1QzT0JDoXF/pxzqO+FkxvwkHZo9k0NJYDedg==} - cpu: [riscv64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@rollup/rollup-linux-x64-gnu@4.12.1: - resolution: {integrity: sha512-nD3YcUv6jBJbBNFvSbp0IV66+ba/1teuBcu+fBBPZ33sidxitc6ErhON3JNavaH8HlswhWMC3s5rgZpM4MtPqQ==} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@rollup/rollup-linux-x64-musl@4.12.1: - resolution: {integrity: sha512-7/XVZqgBby2qp/cO0TQ8uJK+9xnSdJ9ct6gSDdEr4MfABrjTyrW6Bau7HQ73a2a5tPB7hno49A0y1jhWGDN9OQ==} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@rollup/rollup-win32-arm64-msvc@4.12.1: - resolution: {integrity: sha512-CYc64bnICG42UPL7TrhIwsJW4QcKkIt9gGlj21gq3VV0LL6XNb1yAdHVp1pIi9gkts9gGcT3OfUYHjGP7ETAiw==} - cpu: [arm64] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /@rollup/rollup-win32-ia32-msvc@4.12.1: - resolution: {integrity: sha512-LN+vnlZ9g0qlHGlS920GR4zFCqAwbv2lULrR29yGaWP9u7wF5L7GqWu9Ah6/kFZPXPUkpdZwd//TNR+9XC9hvA==} - cpu: [ia32] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /@rollup/rollup-win32-x64-msvc@4.12.1: - resolution: {integrity: sha512-n+vkrSyphvmU0qkQ6QBNXCGr2mKjhP08mPRM/Xp5Ck2FV4NrHU+y6axzDeixUrCBHVUS51TZhjqrKBBsHLKb2Q==} - cpu: [x64] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /@sinclair/typebox@0.27.8: - resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} - dev: true - - /@types/estree@1.0.5: - resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} - dev: true - - /@types/negotiator@0.6.3: - resolution: {integrity: sha512-JkXTOdKs5MF086b/pt8C3+yVp3iDUwG635L7oCH6HvJvvr6lSUU5oe/gLXnPEfYRROHjJIPgCV6cuAg8gGkntQ==} - dev: true - - /@types/node@20.11.30: - resolution: {integrity: sha512-dHM6ZxwlmuZaRmUPfv1p+KrdD1Dci04FbdEm/9wEMouFqxYoFl5aMkt0VMAUtYRQDyYvD41WJLukhq/ha3YuTw==} - dependencies: - undici-types: 5.26.5 - dev: true - - /@vitest/expect@1.4.0: - resolution: {integrity: sha512-Jths0sWCJZ8BxjKe+p+eKsoqev1/T8lYcrjavEaz8auEJ4jAVY0GwW3JKmdVU4mmNPLPHixh4GNXP7GFtAiDHA==} - dependencies: - '@vitest/spy': 1.4.0 - '@vitest/utils': 1.4.0 - chai: 4.4.1 - dev: true - - /@vitest/runner@1.4.0: - resolution: {integrity: sha512-EDYVSmesqlQ4RD2VvWo3hQgTJ7ZrFQ2VSJdfiJiArkCerDAGeyF1i6dHkmySqk573jLp6d/cfqCN+7wUB5tLgg==} - dependencies: - '@vitest/utils': 1.4.0 - p-limit: 5.0.0 - pathe: 1.1.2 - dev: true - - /@vitest/snapshot@1.4.0: - resolution: {integrity: sha512-saAFnt5pPIA5qDGxOHxJ/XxhMFKkUSBJmVt5VgDsAqPTX6JP326r5C/c9UuCMPoXNzuudTPsYDZCoJ5ilpqG2A==} - dependencies: - magic-string: 0.30.8 - pathe: 1.1.2 - pretty-format: 29.7.0 - dev: true - - /@vitest/spy@1.4.0: - resolution: {integrity: sha512-Ywau/Qs1DzM/8Uc+yA77CwSegizMlcgTJuYGAi0jujOteJOUf1ujunHThYo243KG9nAyWT3L9ifPYZ5+As/+6Q==} - dependencies: - tinyspy: 2.2.1 - dev: true - - /@vitest/utils@1.4.0: - resolution: {integrity: sha512-mx3Yd1/6e2Vt/PUC98DcqTirtfxUyAZ32uK82r8rZzbtBeBo+nqgnjx/LvqQdWsrvNtm14VmurNgcf4nqY5gJg==} - dependencies: - diff-sequences: 29.6.3 - estree-walker: 3.0.3 - loupe: 2.3.7 - pretty-format: 29.7.0 - dev: true - - /acorn-walk@8.3.2: - resolution: {integrity: sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==} - engines: {node: '>=0.4.0'} - dev: true - - /acorn@8.11.3: - resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==} - engines: {node: '>=0.4.0'} - hasBin: true - dev: true - - /ansi-regex@5.0.1: - resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} - engines: {node: '>=8'} - dev: true - - /ansi-regex@6.0.1: - resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} - engines: {node: '>=12'} - dev: true - - /ansi-styles@4.3.0: - resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} - engines: {node: '>=8'} - dependencies: - color-convert: 2.0.1 - dev: true - - /ansi-styles@5.2.0: - resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} - engines: {node: '>=10'} - dev: true - - /assertion-error@1.1.0: - resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} - dev: true - - /base64-js@1.5.1: - resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - dev: true - - /bl@5.1.0: - resolution: {integrity: sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==} - dependencies: - buffer: 6.0.3 - inherits: 2.0.4 - readable-stream: 3.6.2 - dev: true - - /buffer@6.0.3: - resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} - dependencies: - base64-js: 1.5.1 - ieee754: 1.2.1 - dev: true - - /cac@6.7.14: - resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} - engines: {node: '>=8'} - dev: true - - /camelcase@5.3.1: - resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} - engines: {node: '>=6'} - dev: true - - /chai@4.4.1: - resolution: {integrity: sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==} - engines: {node: '>=4'} - 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.7 - pathval: 1.1.1 - type-detect: 4.0.8 - dev: true - - /chalk@5.3.0: - resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} - engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} - dev: true - - /check-error@1.0.3: - resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} - dependencies: - get-func-name: 2.0.2 - dev: true - - /cli-cursor@4.0.0: - resolution: {integrity: sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dependencies: - restore-cursor: 4.0.0 - dev: true - - /cli-spinners@2.9.2: - resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} - engines: {node: '>=6'} - dev: true - - /cliui@6.0.0: - resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} - dependencies: - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 6.2.0 - dev: true - - /cliui@8.0.1: - resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} - engines: {node: '>=12'} - dependencies: - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 7.0.0 - dev: true - - /color-convert@2.0.1: - resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} - engines: {node: '>=7.0.0'} - dependencies: - color-name: 1.1.4 - dev: true - - /color-name@1.1.4: - resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - dev: true - - /configstore@5.0.1: - resolution: {integrity: sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==} - engines: {node: '>=8'} - dependencies: - dot-prop: 5.3.0 - graceful-fs: 4.2.11 - make-dir: 3.1.0 - unique-string: 2.0.0 - write-file-atomic: 3.0.3 - xdg-basedir: 4.0.0 - dev: true - - /cross-spawn@7.0.3: - resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} - engines: {node: '>= 8'} - dependencies: - path-key: 3.1.1 - shebang-command: 2.0.0 - which: 2.0.2 - dev: true - - /crypto-random-string@2.0.0: - resolution: {integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==} - engines: {node: '>=8'} - dev: true - - /debug@4.3.4: - resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - dependencies: - ms: 2.1.2 - dev: true - - /decamelize@1.2.0: - resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} - engines: {node: '>=0.10.0'} - dev: true - - /deep-eql@4.1.3: - resolution: {integrity: sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==} - engines: {node: '>=6'} - dependencies: - type-detect: 4.0.8 - dev: true - - /degit@2.8.4: - resolution: {integrity: sha512-vqYuzmSA5I50J882jd+AbAhQtgK6bdKUJIex1JNfEUPENCgYsxugzKVZlFyMwV4i06MmnV47/Iqi5Io86zf3Ng==} - engines: {node: '>=8.0.0'} - hasBin: true - dev: true - - /detect-indent@6.1.0: - resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} - engines: {node: '>=8'} - dev: true - - /diff-sequences@29.6.3: - resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dev: true - - /dot-prop@5.3.0: - resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==} - engines: {node: '>=8'} - dependencies: - is-obj: 2.0.0 - dev: true - - /dotenv@16.4.5: - resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} - engines: {node: '>=12'} - dev: true - - /eastasianwidth@0.2.0: - resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - dev: true - - /emoji-regex@10.3.0: - resolution: {integrity: sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==} - dev: true - - /emoji-regex@8.0.0: - resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - dev: true - - /esbuild@0.19.12: - resolution: {integrity: sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==} - engines: {node: '>=12'} - hasBin: true - requiresBuild: true - optionalDependencies: - '@esbuild/aix-ppc64': 0.19.12 - '@esbuild/android-arm': 0.19.12 - '@esbuild/android-arm64': 0.19.12 - '@esbuild/android-x64': 0.19.12 - '@esbuild/darwin-arm64': 0.19.12 - '@esbuild/darwin-x64': 0.19.12 - '@esbuild/freebsd-arm64': 0.19.12 - '@esbuild/freebsd-x64': 0.19.12 - '@esbuild/linux-arm': 0.19.12 - '@esbuild/linux-arm64': 0.19.12 - '@esbuild/linux-ia32': 0.19.12 - '@esbuild/linux-loong64': 0.19.12 - '@esbuild/linux-mips64el': 0.19.12 - '@esbuild/linux-ppc64': 0.19.12 - '@esbuild/linux-riscv64': 0.19.12 - '@esbuild/linux-s390x': 0.19.12 - '@esbuild/linux-x64': 0.19.12 - '@esbuild/netbsd-x64': 0.19.12 - '@esbuild/openbsd-x64': 0.19.12 - '@esbuild/sunos-x64': 0.19.12 - '@esbuild/win32-arm64': 0.19.12 - '@esbuild/win32-ia32': 0.19.12 - '@esbuild/win32-x64': 0.19.12 - - /escalade@3.1.2: - resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} - engines: {node: '>=6'} - dev: true - - /estree-walker@3.0.3: - resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} - dependencies: - '@types/estree': 1.0.5 - dev: true - - /execa@8.0.1: - resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} - engines: {node: '>=16.17'} - 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.3.0 - onetime: 6.0.0 - signal-exit: 4.1.0 - strip-final-newline: 3.0.0 - dev: true - - /find-up@4.1.0: - resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} - engines: {node: '>=8'} - dependencies: - locate-path: 5.0.0 - path-exists: 4.0.0 - dev: true - - /fsevents@2.3.3: - resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} - os: [darwin] - requiresBuild: true - optional: true - - /fuzzy-search@3.2.1: - resolution: {integrity: sha512-vAcPiyomt1ioKAsAL2uxSABHJ4Ju/e4UeDM+g1OlR0vV4YhLGMNsdLNvZTpEDY4JCSt0E4hASCNM5t2ETtsbyg==} - dev: true - - /get-caller-file@2.0.5: - resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} - engines: {node: 6.* || 8.* || >= 10.*} - dev: true - - /get-func-name@2.0.2: - resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} - dev: true - - /get-stream@8.0.1: - resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} - engines: {node: '>=16'} - dev: true - - /get-tsconfig@4.7.3: - resolution: {integrity: sha512-ZvkrzoUA0PQZM6fy6+/Hce561s+faD1rsNwhnO5FelNjyy7EMGJ3Rz1AQ8GYDWjhRs/7dBLOEJvhK8MiEJOAFg==} - dependencies: - resolve-pkg-maps: 1.0.0 - dev: false - - /git-config-path@2.0.0: - resolution: {integrity: sha512-qc8h1KIQbJpp+241id3GuAtkdyJ+IK+LIVtkiFTRKRrmddDzs3SI9CvP1QYmWBFvm1I/PWRwj//of8bgAc0ltA==} - engines: {node: '>=4'} - dev: true - - /graceful-fs@4.2.11: - resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - dev: true - - /human-signals@5.0.0: - resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} - engines: {node: '>=16.17.0'} - dev: true - - /ieee754@1.2.1: - resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - dev: true - - /imurmurhash@0.1.4: - resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} - engines: {node: '>=0.8.19'} - dev: true - - /inherits@2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - dev: true - - /ini@1.3.8: - resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} - dev: true - - /is-fullwidth-code-point@3.0.0: - resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} - engines: {node: '>=8'} - dev: true - - /is-interactive@2.0.0: - resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} - engines: {node: '>=12'} - dev: true - - /is-obj@2.0.0: - resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} - engines: {node: '>=8'} - dev: true - - /is-stream@3.0.0: - resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dev: true - - /is-typedarray@1.0.0: - resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==} - dev: true - - /is-unicode-supported@1.3.0: - resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==} - engines: {node: '>=12'} - dev: true - - /isexe@2.0.0: - resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - dev: true - - /js-tokens@8.0.3: - resolution: {integrity: sha512-UfJMcSJc+SEXEl9lH/VLHSZbThQyLpw1vLO1Lb+j4RWDvG3N2f7yj3PVQA3cmkTBNldJ9eFnM+xEXxHIXrYiJw==} - dev: true - - /jsonc-parser@3.2.1: - resolution: {integrity: sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==} - dev: true - - /kleur@3.0.3: - resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} - engines: {node: '>=6'} - dev: true - - /license@1.0.3: - resolution: {integrity: sha512-M3F6dUcor+vy4znXK5ULfTikeMWxSf/K2w7EUk5vbuZL4UAEN4zOmjh7d2UJ0/v9GYTOz13LNsLqPXJGu+A26w==} - hasBin: true - dependencies: - '@ovyerus/licenses': 6.4.4 - configstore: 5.0.1 - detect-indent: 6.1.0 - fuzzy-search: 3.2.1 - git-config-path: 2.0.0 - parse-git-config: 3.0.0 - prompts: 2.4.2 - wrap-text: 1.0.9 - yargs: 15.4.1 - dev: true - - /local-pkg@0.5.0: - resolution: {integrity: sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==} - engines: {node: '>=14'} - dependencies: - mlly: 1.6.1 - pkg-types: 1.0.3 - dev: true - - /locate-path@5.0.0: - resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} - engines: {node: '>=8'} - dependencies: - p-locate: 4.1.0 - dev: true - - /log-symbols@5.1.0: - resolution: {integrity: sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==} - engines: {node: '>=12'} - dependencies: - chalk: 5.3.0 - is-unicode-supported: 1.3.0 - dev: true - - /loupe@2.3.7: - resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==} - dependencies: - get-func-name: 2.0.2 - dev: true - - /magic-string@0.30.8: - resolution: {integrity: sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==} - engines: {node: '>=12'} - dependencies: - '@jridgewell/sourcemap-codec': 1.4.15 - dev: true - - /make-dir@3.1.0: - resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} - engines: {node: '>=8'} - dependencies: - semver: 6.3.1 - dev: true - - /merge-stream@2.0.0: - resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} - dev: true - - /mimic-fn@2.1.0: - resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} - engines: {node: '>=6'} - dev: true - - /mimic-fn@4.0.0: - resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} - engines: {node: '>=12'} - dev: true - - /mlly@1.6.1: - resolution: {integrity: sha512-vLgaHvaeunuOXHSmEbZ9izxPx3USsk8KCQ8iC+aTlp5sKRSoZvwhHh5L9VbKSaVC6sJDqbyohIS76E2VmHIPAA==} - dependencies: - acorn: 8.11.3 - pathe: 1.1.2 - pkg-types: 1.0.3 - ufo: 1.4.0 - dev: true - - /ms@2.1.2: - resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} - dev: true - - /nanoid@3.3.7: - resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true - dev: true - - /negotiator@0.6.3: - resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} - engines: {node: '>= 0.6'} - dev: false - - /npm-run-path@5.3.0: - resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dependencies: - path-key: 4.0.0 - dev: true - - /onetime@5.1.2: - resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} - engines: {node: '>=6'} - dependencies: - mimic-fn: 2.1.0 - dev: true - - /onetime@6.0.0: - resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} - engines: {node: '>=12'} - dependencies: - mimic-fn: 4.0.0 - dev: true - - /ora@7.0.1: - resolution: {integrity: sha512-0TUxTiFJWv+JnjWm4o9yvuskpEJLXTcng8MJuKd+SzAzp2o+OP3HWqNhB4OdJRt1Vsd9/mR0oyaEYlOnL7XIRw==} - engines: {node: '>=16'} - dependencies: - chalk: 5.3.0 - cli-cursor: 4.0.0 - cli-spinners: 2.9.2 - is-interactive: 2.0.0 - is-unicode-supported: 1.3.0 - log-symbols: 5.1.0 - stdin-discarder: 0.1.0 - string-width: 6.1.0 - strip-ansi: 7.1.0 - dev: true - - /p-limit@2.3.0: - resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} - engines: {node: '>=6'} - dependencies: - p-try: 2.2.0 - dev: true - - /p-limit@5.0.0: - resolution: {integrity: sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==} - engines: {node: '>=18'} - dependencies: - yocto-queue: 1.0.0 - dev: true - - /p-locate@4.1.0: - resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} - engines: {node: '>=8'} - dependencies: - p-limit: 2.3.0 - dev: true - - /p-try@2.2.0: - resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} - engines: {node: '>=6'} - dev: true - - /package-json-type@1.0.3: - resolution: {integrity: sha512-Bey4gdRuOwDbS8Fj1qA3/pTq5r8pqiI5E3tjSqCdhaLSsyGG364VFzXLTIexN5AaNGe/vgdBzLfoKdr7EVg2KQ==} - dev: true - - /parse-git-config@3.0.0: - resolution: {integrity: sha512-wXoQGL1D+2COYWCD35/xbiKma1Z15xvZL8cI25wvxzled58V51SJM04Urt/uznS900iQor7QO04SgdfT/XlbuA==} - engines: {node: '>=8'} - dependencies: - git-config-path: 2.0.0 - ini: 1.3.8 - dev: true - - /path-exists@4.0.0: - resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} - engines: {node: '>=8'} - dev: true - - /path-key@3.1.1: - resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} - engines: {node: '>=8'} - dev: true - - /path-key@4.0.0: - resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} - engines: {node: '>=12'} - dev: true - - /pathe@1.1.2: - resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} - dev: true - - /pathval@1.1.1: - resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} - dev: true - - /picocolors@1.0.0: - resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} - dev: true - - /pkg-types@1.0.3: - resolution: {integrity: sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==} - dependencies: - jsonc-parser: 3.2.1 - mlly: 1.6.1 - pathe: 1.1.2 - dev: true - - /postcss@8.4.35: - resolution: {integrity: sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==} - engines: {node: ^10 || ^12 || >=14} - dependencies: - nanoid: 3.3.7 - picocolors: 1.0.0 - source-map-js: 1.0.2 - dev: true - - /pretty-bytes@6.1.1: - resolution: {integrity: sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ==} - engines: {node: ^14.13.1 || >=16.0.0} - dev: true - - /pretty-format@29.7.0: - resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/schemas': 29.6.3 - ansi-styles: 5.2.0 - react-is: 18.2.0 - dev: true - - /pridepack@2.6.0(tslib@2.6.2)(typescript@5.4.3): - resolution: {integrity: sha512-K81TouT+M3zwzPvDi70/CFVtzADvGpn071zAMm419ULb29gZni21pJ24njDFm3O+lJn0txBl4x1dsFBLWqS4iQ==} - engines: {node: '>=16'} - hasBin: true - peerDependencies: - tslib: ^2.0 - typescript: ^5.0 - dependencies: - '@ovyerus/licenses': 6.4.4 - degit: 2.8.4 - dotenv: 16.4.5 - esbuild: 0.19.12 - execa: 8.0.1 - license: 1.0.3 - ora: 7.0.1 - package-json-type: 1.0.3 - pretty-bytes: 6.1.1 - prompts: 2.4.2 - tslib: 2.6.2 - typescript: 5.4.3 - yargs: 17.7.2 - dev: true - - /prompts@2.4.2: - resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} - engines: {node: '>= 6'} - dependencies: - kleur: 3.0.3 - sisteransi: 1.0.5 - dev: true - - /react-is@18.2.0: - resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} - dev: true - - /readable-stream@3.6.2: - resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} - engines: {node: '>= 6'} - dependencies: - inherits: 2.0.4 - string_decoder: 1.3.0 - util-deprecate: 1.0.2 - dev: true - - /require-directory@2.1.1: - resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} - engines: {node: '>=0.10.0'} - dev: true - - /require-main-filename@2.0.0: - resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} - dev: true - - /resolve-pkg-maps@1.0.0: - resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} - dev: false - - /restore-cursor@4.0.0: - resolution: {integrity: sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dependencies: - onetime: 5.1.2 - signal-exit: 3.0.7 - dev: true - - /rollup@4.12.1: - resolution: {integrity: sha512-ggqQKvx/PsB0FaWXhIvVkSWh7a/PCLQAsMjBc+nA2M8Rv2/HG0X6zvixAB7KyZBRtifBUhy5k8voQX/mRnABPg==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} - hasBin: true - dependencies: - '@types/estree': 1.0.5 - optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.12.1 - '@rollup/rollup-android-arm64': 4.12.1 - '@rollup/rollup-darwin-arm64': 4.12.1 - '@rollup/rollup-darwin-x64': 4.12.1 - '@rollup/rollup-linux-arm-gnueabihf': 4.12.1 - '@rollup/rollup-linux-arm64-gnu': 4.12.1 - '@rollup/rollup-linux-arm64-musl': 4.12.1 - '@rollup/rollup-linux-riscv64-gnu': 4.12.1 - '@rollup/rollup-linux-x64-gnu': 4.12.1 - '@rollup/rollup-linux-x64-musl': 4.12.1 - '@rollup/rollup-win32-arm64-msvc': 4.12.1 - '@rollup/rollup-win32-ia32-msvc': 4.12.1 - '@rollup/rollup-win32-x64-msvc': 4.12.1 - fsevents: 2.3.3 - dev: true - - /safe-buffer@5.2.1: - resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - dev: true - - /semver@6.3.1: - resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} - hasBin: true - dev: true - - /set-blocking@2.0.0: - resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} - dev: true - - /shebang-command@2.0.0: - resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} - engines: {node: '>=8'} - dependencies: - shebang-regex: 3.0.0 - dev: true - - /shebang-regex@3.0.0: - resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} - engines: {node: '>=8'} - dev: true - - /siginfo@2.0.0: - resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} - dev: true - - /signal-exit@3.0.7: - resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} - dev: true - - /signal-exit@4.1.0: - resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} - engines: {node: '>=14'} - dev: true - - /sisteransi@1.0.5: - resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} - dev: true - - /source-map-js@1.0.2: - resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} - engines: {node: '>=0.10.0'} - dev: true - - /stackback@0.0.2: - resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} - dev: true - - /std-env@3.7.0: - resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==} - dev: true - - /stdin-discarder@0.1.0: - resolution: {integrity: sha512-xhV7w8S+bUwlPTb4bAOUQhv8/cSS5offJuX8GQGq32ONF0ZtDWKfkdomM3HMRA+LhX6um/FZ0COqlwsjD53LeQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dependencies: - bl: 5.1.0 - dev: true - - /string-width@4.2.3: - resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} - engines: {node: '>=8'} - dependencies: - emoji-regex: 8.0.0 - is-fullwidth-code-point: 3.0.0 - strip-ansi: 6.0.1 - dev: true - - /string-width@6.1.0: - resolution: {integrity: sha512-k01swCJAgQmuADB0YIc+7TuatfNvTBVOoaUWJjTB9R4VJzR5vNWzf5t42ESVZFPS8xTySF7CAdV4t/aaIm3UnQ==} - engines: {node: '>=16'} - dependencies: - eastasianwidth: 0.2.0 - emoji-regex: 10.3.0 - strip-ansi: 7.1.0 - dev: true - - /string_decoder@1.3.0: - resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} - dependencies: - safe-buffer: 5.2.1 - dev: true - - /strip-ansi@6.0.1: - resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} - engines: {node: '>=8'} - dependencies: - ansi-regex: 5.0.1 - dev: true - - /strip-ansi@7.1.0: - resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} - engines: {node: '>=12'} - dependencies: - ansi-regex: 6.0.1 - dev: true - - /strip-final-newline@3.0.0: - resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} - engines: {node: '>=12'} - dev: true - - /strip-literal@2.0.0: - resolution: {integrity: sha512-f9vHgsCWBq2ugHAkGMiiYY+AYG0D/cbloKKg0nhaaaSNsujdGIpVXCNsrJpCKr5M0f4aI31mr13UjY6GAuXCKA==} - dependencies: - js-tokens: 8.0.3 - dev: true - - /tinybench@2.6.0: - resolution: {integrity: sha512-N8hW3PG/3aOoZAN5V/NSAEDz0ZixDSSt5b/a05iqtpgfLWMSVuCo7w0k2vVvEjdrIoeGqZzweX2WlyioNIHchA==} - dev: true - - /tinypool@0.8.2: - resolution: {integrity: sha512-SUszKYe5wgsxnNOVlBYO6IC+8VGWdVGZWAqUxp3UErNBtptZvWbwyUOyzNL59zigz2rCA92QiL3wvG+JDSdJdQ==} - engines: {node: '>=14.0.0'} - dev: true - - /tinyspy@2.2.1: - resolution: {integrity: sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==} - engines: {node: '>=14.0.0'} - dev: true - - /tslib@2.6.2: - resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} - dev: true - - /tsx@4.7.1: - resolution: {integrity: sha512-8d6VuibXHtlN5E3zFkgY8u4DX7Y3Z27zvvPKVmLon/D4AjuKzarkUBTLDBgj9iTQ0hg5xM7c/mYiRVM+HETf0g==} - engines: {node: '>=18.0.0'} - hasBin: true - dependencies: - esbuild: 0.19.12 - get-tsconfig: 4.7.3 - optionalDependencies: - fsevents: 2.3.3 - dev: false - - /type-detect@4.0.8: - resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} - engines: {node: '>=4'} - dev: true - - /typedarray-to-buffer@3.1.5: - resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==} - dependencies: - is-typedarray: 1.0.0 - dev: true - - /typescript@5.4.3: - resolution: {integrity: sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==} - engines: {node: '>=14.17'} - hasBin: true - dev: true - - /ufo@1.4.0: - resolution: {integrity: sha512-Hhy+BhRBleFjpJ2vchUNN40qgkh0366FWJGqVLYBHev0vpHTrXSA0ryT+74UiW6KWsldNurQMKGqCm1M2zBciQ==} - dev: true - - /undici-types@5.26.5: - resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} - dev: true - - /unique-string@2.0.0: - resolution: {integrity: sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==} - engines: {node: '>=8'} - dependencies: - crypto-random-string: 2.0.0 - dev: true - - /util-deprecate@1.0.2: - resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - dev: true - - /valibot@0.30.0: - resolution: {integrity: sha512-5POBdbSkM+3nvJ6ZlyQHsggisfRtyT4tVTo1EIIShs6qCdXJnyWU5TJ68vr8iTg5zpOLjXLRiBqNx+9zwZz/rA==} - dev: false - - /vite-node@1.4.0(@types/node@20.11.30): - resolution: {integrity: sha512-VZDAseqjrHgNd4Kh8icYHWzTKSCZMhia7GyHfhtzLW33fZlG9SwsB6CEhgyVOWkJfJ2pFLrp/Gj1FSfAiqH9Lw==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true - dependencies: - cac: 6.7.14 - debug: 4.3.4 - pathe: 1.1.2 - picocolors: 1.0.0 - vite: 5.1.6(@types/node@20.11.30) - transitivePeerDependencies: - - '@types/node' - - less - - lightningcss - - sass - - stylus - - sugarss - - supports-color - - terser - dev: true - - /vite@5.1.6(@types/node@20.11.30): - resolution: {integrity: sha512-yYIAZs9nVfRJ/AiOLCA91zzhjsHUgMjB+EigzFb6W2XTLO8JixBCKCjvhKZaye+NKYHCrkv3Oh50dH9EdLU2RA==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true - peerDependencies: - '@types/node': ^18.0.0 || >=20.0.0 - less: '*' - lightningcss: ^1.21.0 - sass: '*' - stylus: '*' - sugarss: '*' - terser: ^5.4.0 - peerDependenciesMeta: - '@types/node': - optional: true - less: - optional: true - lightningcss: - optional: true - sass: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true - dependencies: - '@types/node': 20.11.30 - esbuild: 0.19.12 - postcss: 8.4.35 - rollup: 4.12.1 - optionalDependencies: - fsevents: 2.3.3 - dev: true - - /vitest@1.4.0(@types/node@20.11.30): - resolution: {integrity: sha512-gujzn0g7fmwf83/WzrDTnncZt2UiXP41mHuFYFrdwaLRVQ6JYQEiME2IfEjU3vcFL3VKa75XhI3lFgn+hfVsQw==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true - peerDependencies: - '@edge-runtime/vm': '*' - '@types/node': ^18.0.0 || >=20.0.0 - '@vitest/browser': 1.4.0 - '@vitest/ui': 1.4.0 - 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 - dependencies: - '@types/node': 20.11.30 - '@vitest/expect': 1.4.0 - '@vitest/runner': 1.4.0 - '@vitest/snapshot': 1.4.0 - '@vitest/spy': 1.4.0 - '@vitest/utils': 1.4.0 - acorn-walk: 8.3.2 - chai: 4.4.1 - debug: 4.3.4 - execa: 8.0.1 - local-pkg: 0.5.0 - magic-string: 0.30.8 - pathe: 1.1.2 - picocolors: 1.0.0 - std-env: 3.7.0 - strip-literal: 2.0.0 - tinybench: 2.6.0 - tinypool: 0.8.2 - vite: 5.1.6(@types/node@20.11.30) - vite-node: 1.4.0(@types/node@20.11.30) - why-is-node-running: 2.2.2 - transitivePeerDependencies: - - less - - lightningcss - - sass - - stylus - - sugarss - - supports-color - - terser - dev: true - - /which-module@2.0.1: - resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} - dev: true - - /which@2.0.2: - resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} - engines: {node: '>= 8'} - hasBin: true - dependencies: - isexe: 2.0.0 - dev: true - - /why-is-node-running@2.2.2: - resolution: {integrity: sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==} - engines: {node: '>=8'} - hasBin: true - dependencies: - siginfo: 2.0.0 - stackback: 0.0.2 - dev: true - - /wrap-ansi@6.2.0: - resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} - engines: {node: '>=8'} - dependencies: - ansi-styles: 4.3.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - dev: true - - /wrap-ansi@7.0.0: - resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} - engines: {node: '>=10'} - dependencies: - ansi-styles: 4.3.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - dev: true - - /wrap-text@1.0.9: - resolution: {integrity: sha512-NWfjspSgMDXQIMpKM56AwCQPI01OMFRYYJBh6dGNCfH7AOl+j/VqqbiopgJ4VuQfSluqLc+2ekqaPNpYAGZ/Vg==} - dev: true - - /write-file-atomic@3.0.3: - resolution: {integrity: sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==} - dependencies: - imurmurhash: 0.1.4 - is-typedarray: 1.0.0 - signal-exit: 3.0.7 - typedarray-to-buffer: 3.1.5 - dev: true - - /xdg-basedir@4.0.0: - resolution: {integrity: sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==} - engines: {node: '>=8'} - dev: true - - /y18n@4.0.3: - resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} - dev: true - - /y18n@5.0.8: - resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} - engines: {node: '>=10'} - dev: true - - /yargs-parser@18.1.3: - resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} - engines: {node: '>=6'} - dependencies: - camelcase: 5.3.1 - decamelize: 1.2.0 - dev: true - - /yargs-parser@21.1.1: - resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} - engines: {node: '>=12'} - dev: true - - /yargs@15.4.1: - resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==} - engines: {node: '>=8'} - dependencies: - cliui: 6.0.0 - decamelize: 1.2.0 - find-up: 4.1.0 - get-caller-file: 2.0.5 - require-directory: 2.1.1 - require-main-filename: 2.0.0 - set-blocking: 2.0.0 - string-width: 4.2.3 - which-module: 2.0.1 - y18n: 4.0.3 - yargs-parser: 18.1.3 - dev: true - - /yargs@17.7.2: - resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} - engines: {node: '>=12'} - dependencies: - cliui: 8.0.1 - escalade: 3.1.2 - get-caller-file: 2.0.5 - require-directory: 2.1.1 - string-width: 4.2.3 - y18n: 5.0.8 - yargs-parser: 21.1.1 - dev: true - - /yocto-queue@1.0.0: - resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==} - engines: {node: '>=12.20'} - dev: true diff --git a/packages/core/pridepack.json b/packages/core/pridepack.json index 0ef3915..0bc7a8f 100644 --- a/packages/core/pridepack.json +++ b/packages/core/pridepack.json @@ -1,8 +1,3 @@ { - "target": "es2018", - "entrypoints": { - ".": "src/common/index.ts", - "./backend": "src/backend/index.ts", - "./client": "src/client/index.ts" - } + "target": "es2018" } diff --git a/packages/core/src/backend/common.ts b/packages/core/src/backend/common.ts deleted file mode 100644 index 80cfdec..0000000 --- a/packages/core/src/backend/common.ts +++ /dev/null @@ -1,87 +0,0 @@ -import {BaseSchema} from 'valibot'; -import { - ApplicationState, - BaseResourceType, - ContentNegotiation, - Language, - LanguageStatusMessageMap, - Resource, -} from '../common'; -import {DataSource} from './data-source'; - -export interface Server { - requestDecorator(requestDecorator: RequestDecorator): this; -} - -export interface BackendState { - app: ApplicationState; - dataSource: DataSource; - cn: ContentNegotiation; - showTotalItemCountOnGetCollection: boolean; - throwsErrorOnDeletingNotFound: boolean; - checksSerializersOnDelete: boolean; - showTotalItemCountOnCreateItem: boolean; -} - -export interface RequestContext {} - -export interface Middleware {} - -export class MiddlewareError extends Error {} - -export interface MiddlewareResponseErrorParams extends Omit { - cause?: unknown; -} - -export abstract class MiddlewareResponseError extends MiddlewareError implements Response { - readonly statusMessage: Response['statusMessage']; - readonly statusCode: Response['statusCode']; - readonly headers: Response['headers']; - - constructor(statusMessage: keyof Language['statusMessages'], params: MiddlewareResponseErrorParams) { - super(statusMessage, { cause: params.cause }); - this.statusCode = params.statusCode; - this.headers = params.headers; - this.statusMessage = statusMessage; - } -} - -export type RequestDecorator = (req: RequestContext) => RequestContext | Promise; - -export type ParamRequestDecorator = []> = (...args: Params) => RequestDecorator; - -// TODO put this in HTTP -export type Method = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS' | 'HEAD' | 'QUERY'; - -export interface AllowedMiddlewareSpecification { - method: Method; - middleware: Middleware; - constructBodySchema?: (resource: Resource, resourceId?: string) => BaseSchema; - allowed: (resource: Resource) => boolean; -} - -export interface Response { - // type of response - statusCode: number; - - // description of response - statusMessage?: keyof LanguageStatusMessageMap; - - // metadata of the response - headers?: Record; -} - -export interface Backend { - showTotalItemCountOnGetCollection(b?: boolean): this; - showTotalItemCountOnCreateItem(b?: boolean): this; - checksSerializersOnDelete(b?: boolean): this; - throwsErrorOnDeletingNotFound(b?: boolean): this; - use(extender: (state: BackendState, t: this) => BackendExtended): BackendExtended; - createServer(type: string, options?: {}): T; - dataSource?: (resource: Resource) => T; -} - -export const getAllowString = (middlewares: AllowedMiddlewareSpecification[]) => { - const allowedMethods = middlewares.flatMap((m) => m.method === 'GET' ? [m.method, 'HEAD'] : [m.method]); - return allowedMethods.join(','); -} diff --git a/packages/core/src/backend/core.ts b/packages/core/src/backend/core.ts deleted file mode 100644 index e7e31f2..0000000 --- a/packages/core/src/backend/core.ts +++ /dev/null @@ -1,57 +0,0 @@ -import {ApplicationState, FALLBACK_CHARSET, FALLBACK_LANGUAGE, FALLBACK_MEDIA_TYPE} from '../common'; -import {Backend, BackendState, Server} from './common'; -import {DataSource} from './data-source'; - -export interface BackendExtender = Backend, BB extends B = B> { - (state: BackendState, backend: B): BB; -} - -export interface CreateBackendParams { - app: ApplicationState; - dataSource: T; -} - -export const createBackend = (params: CreateBackendParams) => { - const backendState: BackendState = { - app: params.app, - dataSource: params.dataSource, - cn: { - language: FALLBACK_LANGUAGE, - charset: FALLBACK_CHARSET, - mediaType: FALLBACK_MEDIA_TYPE, - }, - showTotalItemCountOnGetCollection: false, - showTotalItemCountOnCreateItem: false, - throwsErrorOnDeletingNotFound: false, - checksSerializersOnDelete: false, - }; - - return { - showTotalItemCountOnGetCollection(b = true) { - backendState.showTotalItemCountOnGetCollection = b; - return this; - }, - showTotalItemCountOnCreateItem(b = true) { - backendState.showTotalItemCountOnCreateItem = b; - return this; - }, - throwsErrorOnDeletingNotFound(b = true) { - backendState.throwsErrorOnDeletingNotFound = b; - return this; - }, - checksSerializersOnDelete(b = true) { - backendState.checksSerializersOnDelete = b; - return this; - }, - createServer(_type: string, _options = {}) { - return { - requestDecorator() { - return this; - }, - } as unknown as T; - }, - use(extender) { - return extender(backendState, this); - }, - } satisfies Backend; -}; diff --git a/packages/core/src/backend/data-source.ts b/packages/core/src/backend/data-source.ts deleted file mode 100644 index 6343a05..0000000 --- a/packages/core/src/backend/data-source.ts +++ /dev/null @@ -1,39 +0,0 @@ -import * as v from 'valibot'; -import {Resource, QueryAndGrouping} from '../common'; - -type IsCreated = boolean; - -type TotalCount = number; - -type DeleteResult = unknown; - -export type DataSourceQuery = QueryAndGrouping; - -export interface DataSource< - ItemData extends object = object, - ID extends unknown = unknown, - Query extends DataSourceQuery = DataSourceQuery, -> { - initialize(): Promise; - getTotalCount?(query?: Query): Promise; - getMultiple(query?: Query): Promise; - getById(id: ID): Promise; - getSingle?(query?: Query): Promise; - create(data: ItemData): Promise; - delete(id: ID): Promise; - emplace(id: ID, data: ItemData): Promise<[ItemData, IsCreated]>; - patch(id: ID, data: Partial): Promise; - prepareResource(resource: Resource): void; - newId(): Promise; -} - -export interface ResourceIdConfig { - generationStrategy: GenerationStrategy; - serialize: (id: unknown) => string; - deserialize: (id: string) => v.Output; - schema: IdSchema; -} - -export interface GenerationStrategy { - (dataSource: DataSource, ...args: unknown[]): Promise; -} diff --git a/packages/core/src/backend/index.ts b/packages/core/src/backend/index.ts index 1243f85..4fc38f5 100644 --- a/packages/core/src/backend/index.ts +++ b/packages/core/src/backend/index.ts @@ -1,3 +1,21 @@ -export * from './core'; -export * from './common'; -export * from './data-source'; +import { App as BaseApp } from '../common/app'; + +interface BackendParams { + app: App; +} + +export interface Backend { + app: App; +} + +class BackendInstance implements Backend { + readonly app: App; + + constructor(params: BackendParams) { + this.app = params.app; + } +} + +export const backend = (params: BackendParams): Backend => { + return new BackendInstance(params); +}; diff --git a/packages/core/src/backend/server.ts b/packages/core/src/backend/server.ts new file mode 100644 index 0000000..4cd8860 --- /dev/null +++ b/packages/core/src/backend/server.ts @@ -0,0 +1,34 @@ +import { Backend as BaseBackend } from './index'; +import http from 'http'; + +interface ServerParams { + backend: Backend; +} + +export interface Server { + backend: Backend; + host(params: ServiceParams): this; +} + +class ServerInstance implements Server { + readonly backend: Backend; + private readonly serverInternal; + + constructor(params: ServerParams) { + this.backend = params.backend; + this.serverInternal = new http.Server(this.requestListener); + } + + private readonly requestListener = (req, res) => { + + }; + + host(params: ServiceParams) { + this.serverInternal.listen(params.port, params.host); + return this; + } +} + +export const server = (params: ServerParams): Server => { + return new ServerInstance(params); +}; diff --git a/packages/core/src/client/index.ts b/packages/core/src/client/index.ts index 9fe20ae..54270da 100644 --- a/packages/core/src/client/index.ts +++ b/packages/core/src/client/index.ts @@ -1,53 +1,28 @@ -import { - ApplicationState, - Charset, - FALLBACK_CHARSET, - FALLBACK_LANGUAGE, - FALLBACK_MEDIA_TYPE, - Language, - MediaType, -} from '../common'; +import { App as BaseApp } from '../common/app'; -export interface ClientState { - app: ApplicationState; - mediaType: MediaType; - charset: Charset; - language: Language; +interface ClientParams { + app: App; } -export interface ClientBuilder { - language(languageCode: ClientState['language']['name']): this; - charset(charset: ClientState['charset']['name']): this; - mediaType(mediaType: ClientState['mediaType']['name']): this; +interface Client { + app: App; + connect(params: ServiceParams): this; } -export interface CreateClientParams { - app: ApplicationState; -} +class ClientInstance implements Client { + readonly app: App; + private connection: ServiceParams; + + constructor(params: ClientParams) { + this.app = params.app; + } -export const createClient = (params: CreateClientParams) => { - const clientState: ClientState = { - app: params.app, - mediaType: FALLBACK_MEDIA_TYPE, - charset: FALLBACK_CHARSET, - language: FALLBACK_LANGUAGE - }; + connect(params: ServiceParams) { + this.connection = params; + return this; + } +} - return { - mediaType(mediaTypeName) { - const mediaType = clientState.app.mediaTypes.get(mediaTypeName); - clientState.mediaType = mediaType ?? FALLBACK_MEDIA_TYPE; - return this; - }, - charset(charsetName) { - const charset = clientState.app.charsets.get(charsetName); - clientState.charset = charset ?? FALLBACK_CHARSET; - return this; - }, - language(languageCode) { - const language = clientState.app.languages.get(languageCode); - clientState.language = language ?? FALLBACK_LANGUAGE; - return this; - } - } satisfies ClientBuilder; +export const client = (params: ClientParams): Client => { + return new ClientInstance(params); }; diff --git a/packages/core/src/common/app.ts b/packages/core/src/common/app.ts index 94beaf1..fe28fab 100644 --- a/packages/core/src/common/app.ts +++ b/packages/core/src/common/app.ts @@ -1,100 +1,223 @@ -import {BaseResourceType, Resource} from './resource'; -import {FALLBACK_LANGUAGE, Language} from './language'; -import {FALLBACK_MEDIA_TYPE, MediaType, PATCH_CONTENT_TYPES} from './media-type'; -import {Charset, FALLBACK_CHARSET} from './charset'; import * as v from 'valibot'; -import {Backend, createBackend, CreateBackendParams} from '../backend'; -import {ClientBuilder, createClient, CreateClientParams} from '../client'; -type ApplicationMap = Map; +interface BaseEndpointState { + operations: unknown; +} + +type OpValueType = undefined | boolean; -export interface ApplicationState { - name: string; - resources: Set>; - languages: ApplicationMap; - mediaTypes: ApplicationMap; - charsets: ApplicationMap; +export interface Endpoint { + schema: Schema; + can( + op: OpName, + value?: OpValue + ): Endpoint< + Schema, + { + operations: State['operations'] extends string[] ? readonly [...State['operations'], OpName] : [OpName], + } + >; } -export interface ApplicationParams { - name: string; +interface EndpointParams { + schema: Schema; } -export interface Application< - Resources extends Resource[] = [], - MediaTypes extends MediaType[] = [], - Charsets extends Charset[] = [], - Languages extends Language[] = [] -> { - mediaType(mediaType: MediaType): Application< - Resources, [...MediaTypes, MediaType], Charsets, Languages - >; - language(language: Language): Application< - Resources, MediaTypes, Charsets, [...Languages, Language] - >; - charset(charset: Charset): Application< - Resources, MediaTypes, [...Charsets, Charset], Languages +class EndpointInstance< + Params extends EndpointParams, + State extends BaseEndpointState +> implements Endpoint { + readonly operations: Set; + readonly schema: Params['schema']; + + constructor(params: Params) { + this.schema = params.schema; + this.operations = new Set(); + } + + can( + op: OpName, + value?: OpValue + ): Endpoint< + Params['schema'], + { + operations: State['operations'] extends string[] ? readonly [...State['operations'], OpName] : [OpName], + } + > { + if (value) { + this.operations.add(op); + } else { + this.operations.delete(op); + } + + return this; + } +} + +export const endpoint = (params: Params): Endpoint> => { + return new EndpointInstance(params); +}; + +export interface BaseAppState { + endpoints: unknown; + operations: unknown; +} + +type EndpointOperations = T extends Endpoint ? ( + R extends { operations: Record } ? R['operations'][number] : [] + ) : []; + +type AppOperations = ( + T extends App + ? R extends BaseAppState + ? keyof R['operations'] + : never + : never + ); + + + +export interface App; +}> { + name: Name; + operation< + OperationName extends string, + OperationParams extends BaseOperationParams, + NewOperation extends Operation + >(newOperation: NewOperation): App< + Name, + { + endpoints: State['endpoints'], + operations: keyof State['operations'] extends never ? { + [Key in NewOperation['name']]: ( + Exclude extends readonly string[] ? Exclude : never[] + ) + } : { + [Key in NewOperation['name'] | keyof State['operations']]: ( + State['operations'] extends Record + ? ( + State['operations'][Key] extends readonly string[] ? State['operations'][Key] : ( + Exclude extends readonly string[] ? Exclude : never[] + ) + ) + : ( + Exclude extends readonly string[] ? Exclude : never[] + ) + ); + } + } >; - resource(resRaw: Resource): Application< - [...Resources, Resource], MediaTypes, Charsets, Languages + endpoint(newEndpoint: EndpointOperations extends AppOperations ? NewEndpoint : never): App< + Name, + { + endpoints: State['endpoints'] extends Array ? [...State['endpoints'], NewEndpoint] : [NewEndpoint], + operations: State['operations'] + } >; - createBackend(params: Omit): Backend; - createClient(params: Omit): ClientBuilder; } -export const application = (appParams: ApplicationParams): Application => { - const appState: ApplicationState = { - name: appParams.name, - resources: new Set(), - languages: new Map([ - [FALLBACK_LANGUAGE.name, FALLBACK_LANGUAGE], - ]), - mediaTypes: new Map([ - [FALLBACK_MEDIA_TYPE.name, FALLBACK_MEDIA_TYPE], - ...( - PATCH_CONTENT_TYPES.map((name) => [ - name as MediaType['name'], - { - serialize: (s: unknown) => JSON.stringify(s), - deserialize: (s: string) => JSON.parse(s), - name: name as MediaType['name'], - } satisfies MediaType - ] as [MediaType['name'], MediaType]) - ), - ]), - charsets: new Map([ - [FALLBACK_CHARSET.name, FALLBACK_CHARSET], - ]), - }; - - return { - mediaType(mediaType: MediaType) { - appState.mediaTypes.set(mediaType.name, mediaType); - return this; - }, - charset(charset: Charset) { - appState.charsets.set(charset.name, charset); - return this; - }, - language(language: Language) { - appState.languages.set(language.name, language); - return this; - }, - resource(resRaw: Resource) { - appState.resources.add(resRaw); - return this; - }, - createBackend(params: Omit) { - return createBackend({ - ...params, - app: appState - }); - }, - createClient(params: Omit) { - return createClient({ - ...params, - app: appState - }); - }, - }; +interface BaseOperationParams { + name: Name; + args?: Args; +} + +interface Operation { + name: Params['name']; + args: Params['args']; +} + +class OperationInstance implements Operation { + readonly name: Params['name']; + readonly args: Params['args']; + + constructor(params: Params) { + this.name = params.name; + this.args = params.args; + } +} + +export const operation = ( + params: Params +): Operation => { + return new OperationInstance(params); }; + +interface AppParams { + name: Name; +} + +class AppInstance implements App { + readonly name: Params['name']; + readonly endpoints: Set; + readonly operations: Set; + + constructor(params: Params) { + this.name = params.name; + this.endpoints = new Set(); + this.operations = new Set(); + } + + operation(newOperation: NewOperation): App< + Params['name'], + { + endpoints: State['endpoints'], + operations: keyof State['operations'] extends never ? { + [Key in NewOperation['name']]: ( + Exclude extends readonly string[] ? Exclude : never[] + ) + } : { + [Key in NewOperation['name'] | keyof State['operations']]: ( + State['operations'] extends Record + ? ( + State['operations'][Key] extends readonly string[] ? State['operations'][Key] : ( + Exclude extends readonly string[] ? Exclude : never[] + ) + ) + : ( + Exclude extends readonly string[] ? Exclude : never[] + ) + ); + } + } + > { + this.operations.add(newOperation); + return this as App< + Params['name'], + { + endpoints: State['endpoints'], + operations: { + [Key in NewOperation['name'] | keyof State['operations']]: ( + State['operations'] extends Record + ? ( + State['operations'][Key] extends readonly string[] ? State['operations'][Key] : ( + Exclude extends readonly string[] ? Exclude : never[] + ) + ) + : ( + Exclude extends readonly string[] ? Exclude : never[] + ) + ); + } + } + >; + } + + endpoint(newEndpoint: NewEndpoint): App< + Params['name'], + { + endpoints: State['endpoints'] extends Array ? [...State['endpoints'], NewEndpoint] : [NewEndpoint], + operations: State['operations'] + } + > { + this.endpoints.add(newEndpoint); + return this; + } +} + +export const app = (params: Params): App => { + return new AppInstance(params); +}; + +export * as validation from 'valibot'; diff --git a/packages/core/src/common/charset.ts b/packages/core/src/common/charset.ts deleted file mode 100644 index e5086b4..0000000 --- a/packages/core/src/common/charset.ts +++ /dev/null @@ -1,11 +0,0 @@ -export interface Charset { - name: Name; - encode: (str: string) => Buffer; - decode: (buf: Buffer) => string; -} - -export const FALLBACK_CHARSET = { - encode: (str: string) => Buffer.from(str, 'utf-8'), - decode: (buf: Buffer) => buf.toString('utf-8'), - name: 'utf-8' as const, -} satisfies Charset; diff --git a/packages/core/src/common/delta/core.ts b/packages/core/src/common/delta/core.ts deleted file mode 100644 index 68651f5..0000000 --- a/packages/core/src/common/delta/core.ts +++ /dev/null @@ -1,187 +0,0 @@ -import * as v from 'valibot'; -import {getObjectSchema} from './utils'; -import { - InvalidOperationError, - InvalidPathValueError, - InvalidSchemaInPathError, - PathValueTestFailedError, -} from './error'; -import {append, get, remove, set} from './object'; - -const ADD_DELTA_SCHEMA = v.object({ - op: v.literal('add'), - path: v.string(), - value: v.unknown() -}); - -const REMOVE_DELTA_SCHEMA = v.object({ - op: v.literal('remove'), - path: v.string(), -}); - -const REPLACE_DELTA_SCHEMA = v.object({ - op: v.literal('replace'), - path: v.string(), - value: v.unknown() -}); - -const MOVE_DELTA_SCHEMA = v.object({ - op: v.literal('move'), - path: v.string(), - from: v.string(), -}); - -const COPY_DELTA_SCHEMA = v.object({ - op: v.literal('copy'), - path: v.string(), - from: v.string(), -}); - -const TEST_DELTA_SCHEMA = v.object({ - op: v.literal('test'), - path: v.string(), - value: v.unknown() -}); - -export const DELTA_SCHEMA = v.union([ - ADD_DELTA_SCHEMA, - REMOVE_DELTA_SCHEMA, - REPLACE_DELTA_SCHEMA, - MOVE_DELTA_SCHEMA, - COPY_DELTA_SCHEMA, - TEST_DELTA_SCHEMA, -]); - -export type Delta = v.Output; - -const applyReplaceDelta = ( - mutablePreviousObject: Record, - deltaItem: v.Output, - pathSchema: T, -) => { - if (!v.is(pathSchema, deltaItem.value)) { - throw new InvalidPathValueError(); - } - - set(mutablePreviousObject, deltaItem.path, deltaItem.value); -}; - -const applyAddDelta = ( - mutablePreviousObject: Record, - deltaItem: v.Output, - pathSchema: T, -) => { - if (pathSchema.type !== 'array') { - throw new InvalidOperationError(); - } - - const arraySchema = pathSchema as unknown as v.ArraySchema; - if (!v.is(arraySchema.item, deltaItem.value)) { - throw new InvalidPathValueError(); - } - - append(mutablePreviousObject, deltaItem.path, deltaItem.value); -} - -const applyRemoveDelta = ( - mutablePreviousObject: Record, - deltaItem: v.Output, -) => { - remove(mutablePreviousObject, deltaItem.path); -}; - -const applyCopyDelta = ( - mutablePreviousObject: Record, - deltaItem: v.Output, - pathSchema: T, -) => { - const value = get(mutablePreviousObject, deltaItem.from); - if (!v.is(pathSchema, value)) { - throw new InvalidPathValueError(); - } - - set(mutablePreviousObject, deltaItem.path, value); -}; - -const applyMoveDelta = ( - mutablePreviousObject: Record, - deltaItem: v.Output, - pathSchema: T, -) => { - const value = get(mutablePreviousObject, deltaItem.from); - if (!v.is(pathSchema, value)) { - throw new InvalidPathValueError(); - } - - remove(mutablePreviousObject, deltaItem.from) - set(mutablePreviousObject, deltaItem.path, value); -}; - -const applyTestDelta = ( - mutablePreviousObject: Record, - deltaItem: v.Output, -) => { - const value = get(mutablePreviousObject, deltaItem.path); - if (value !== deltaItem.value) { - throw new PathValueTestFailedError(); - } -}; - -type ApplyDeltaFunction = ( - mutablePreviousObject: Record, - deltaItem: any, - pathSchema: T -) => void; - -const OPERATION_FUNCTION_MAP: Record = { - replace: applyReplaceDelta, - add: applyAddDelta, - remove: applyRemoveDelta, - copy: applyCopyDelta, - move: applyMoveDelta, - test: applyTestDelta, -}; - -const mutateObject = ( - mutablePreviousObject: Record, - deltaItem: Delta, - pathSchema: T, -) => { - const { [deltaItem.op]: applyDeltaFn } = OPERATION_FUNCTION_MAP; - - if (typeof applyDeltaFn !== 'function') { - throw new InvalidOperationError(); - } - - applyDeltaFn(mutablePreviousObject, deltaItem, pathSchema); -}; - -export const applyDelta = async ( - existing: Record, - deltaCollection: Delta[], - resourceSchema: T, -) => { - return await deltaCollection.reduce( - async (resultObject, deltaItem) => { - const mutablePreviousObject = await resultObject; - - if (resourceSchema.type !== 'object') { - return mutablePreviousObject; - } - - const resourceObjectSchema = resourceSchema as unknown as v.ObjectSchema; - const pathSchema = getObjectSchema(resourceObjectSchema, deltaItem.path); - if (typeof pathSchema === 'undefined') { - throw new InvalidSchemaInPathError(); - } - - mutateObject(mutablePreviousObject, deltaItem, pathSchema); - if (!v.is(resourceObjectSchema, mutablePreviousObject, { skipPipe: true })) { - throw new InvalidOperationError(); - } - - return mutablePreviousObject; - }, - Promise.resolve(existing), - ); -}; diff --git a/packages/core/src/common/delta/error.ts b/packages/core/src/common/delta/error.ts deleted file mode 100644 index b79e9c8..0000000 --- a/packages/core/src/common/delta/error.ts +++ /dev/null @@ -1,7 +0,0 @@ -export class InvalidSchemaInPathError extends Error {} - -export class InvalidPathValueError extends Error {} - -export class InvalidOperationError extends Error {} - -export class PathValueTestFailedError extends Error {} diff --git a/packages/core/src/common/delta/index.ts b/packages/core/src/common/delta/index.ts deleted file mode 100644 index d47621f..0000000 --- a/packages/core/src/common/delta/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './error'; -export * from './core'; diff --git a/packages/core/src/common/delta/object.ts b/packages/core/src/common/delta/object.ts deleted file mode 100644 index 7bbff69..0000000 --- a/packages/core/src/common/delta/object.ts +++ /dev/null @@ -1,77 +0,0 @@ -import {tokenizePath} from './utils'; - -export const set = (origObject: Record, path: string, value: unknown) => { - if (path.length <= 0) { - return origObject; - } - - const pathFragments = tokenizePath(path); - let cursor = origObject; - let thisPath = pathFragments.shift() as string; - while (pathFragments.length > 0) { - cursor = cursor?.[thisPath] as Record; - thisPath = pathFragments.shift() as string; - } - - if (typeof cursor === 'undefined') { - throw new Error(`Could not set path: ${path}`); - } - - cursor[thisPath] = value; - return origObject; -}; - -export const get = (origObject: Record, path: string) => { - if (path.length <= 0) { - return origObject; - } - const pathFragments = tokenizePath(path); - let cursor = origObject; - let thisPath = pathFragments.shift() as string; - while (pathFragments.length > 0) { - cursor = cursor?.[thisPath] as Record; - thisPath = pathFragments.shift() as string; - } - - return cursor?.[thisPath]; -}; - -export const remove = (origObject: Record, path: string) => { - if (path.length <= 0) { - return origObject; - } - const pathFragments = tokenizePath(path); - let cursor = origObject; - let thisPath = pathFragments.shift() as string; - while (pathFragments.length > 0) { - cursor = cursor?.[thisPath] as Record; - thisPath = pathFragments.shift() as string; - } - - if (typeof cursor === 'undefined') { - throw new Error(`Could not remove on path: ${path}`); - } - - delete cursor[thisPath]; - return origObject; -}; - -export const append = (origObject: Record, path: string, value: unknown) => { - if (path.length <= 0) { - return origObject; - } - const pathFragments = tokenizePath(path); - let cursor = origObject; - let thisPath = pathFragments.shift() as string; - while (pathFragments.length > 0) { - cursor = cursor?.[thisPath] as Record; - thisPath = pathFragments.shift() as string; - } - - if (!Array.isArray(cursor?.[thisPath])) { - throw new Error(`Could not append on path: ${path}`); - } - - (cursor[thisPath] as unknown[]).push(value); - return origObject; -}; diff --git a/packages/core/src/common/delta/utils.ts b/packages/core/src/common/delta/utils.ts deleted file mode 100644 index 0ef9cd3..0000000 --- a/packages/core/src/common/delta/utils.ts +++ /dev/null @@ -1,23 +0,0 @@ -import * as v from 'valibot'; - -export const DELTA_PATH_SEPARATOR = '/' as const; - -export const tokenizePath = (path: string) => { - return path.split(DELTA_PATH_SEPARATOR); -}; - -export const combinePathFragments = (pathFragments: string[]) => { - return pathFragments.join(DELTA_PATH_SEPARATOR); -}; - -export const getObjectSchema = (schema?: v.ObjectSchema, path?: string): v.BaseSchema => { - if (typeof path !== 'string') { - return schema as v.BaseSchema; - } - if (path.length <= 0) { - return schema as v.BaseSchema; - } - const pathFragments = tokenizePath(path); - const thisPath = pathFragments.shift() as string; - return getObjectSchema(schema?.entries?.[thisPath], combinePathFragments(pathFragments)); -}; diff --git a/packages/core/src/common/index.ts b/packages/core/src/common/index.ts index 4865f15..6c4e647 100644 --- a/packages/core/src/common/index.ts +++ b/packages/core/src/common/index.ts @@ -1,18 +1,5 @@ -import {Language} from './language'; -import {Charset} from './charset'; -import {MediaType} from './media-type'; - -export * from './app'; -export * from './charset'; -export * from './delta'; -export * from './media-type'; -export * from './resource'; -export * from './language'; -export * from './queries'; -export * as validation from './validation'; - -export interface ContentNegotiation { - language: Language; - mediaType: MediaType; - charset: Charset; +interface ServiceParams { + host?: string; + port?: number; + basePath?: string; } diff --git a/packages/core/src/common/language.ts b/packages/core/src/common/language.ts deleted file mode 100644 index 92542b7..0000000 --- a/packages/core/src/common/language.ts +++ /dev/null @@ -1,342 +0,0 @@ -export type MessageBody = string | string[] | (string | string[])[]; - -export const LANGUAGE_DEFAULT_ERROR_STATUS_MESSAGE_KEYS = [ - 'unableToInitializeResourceDataSource', - 'unableToFetchResourceCollection', - 'unableToFetchResource', - 'resourceIdNotGiven', - 'languageNotAcceptable', - 'characterSetNotAcceptable', - 'mediaTypeNotAcceptable', - 'methodNotAllowed', - 'urlNotFound', - 'badRequest', - 'deleteNonExistingResource', - 'unableToCreateResource', - 'unableToBindResourceDataSource', - 'unableToGenerateIdFromResourceDataSource', - 'unableToAssignIdFromResourceDataSource', - 'unableToEmplaceResource', - 'unableToSerializeResponse', - 'unableToEncodeResponse', - 'unableToDeleteResource', - 'unableToDeserializeResource', - 'unableToDecodeResource', - 'unableToDeserializeRequest', - 'patchNonExistingResource', - 'unableToPatchResource', - 'invalidResourcePatch', - 'invalidResourcePatchType', - 'invalidResource', - 'notImplemented', - 'internalServerError', - 'resourceNotFound', -] as const; - -export const LANGUAGE_DEFAULT_STATUS_MESSAGE_KEYS = [ - ...LANGUAGE_DEFAULT_ERROR_STATUS_MESSAGE_KEYS, - 'ok', - 'resourceCollectionFetched', - 'resourceCollectionQueried', - 'resourceFetched', - 'resourceDeleted', - 'resourcePatched', - 'resourceCreated', - 'resourceReplaced', - 'provideOptions', -] as const; - -export type LanguageDefaultStatusMessageKey = typeof LANGUAGE_DEFAULT_STATUS_MESSAGE_KEYS[number]; - -export interface LanguageStatusMessageMap extends Record {} - -export type LanguageDefaultErrorStatusMessageKey = typeof LANGUAGE_DEFAULT_ERROR_STATUS_MESSAGE_KEYS[number]; - -export interface LanguageBodyMap extends Record {} - -export interface Language { - name: Name, - statusMessages: LanguageStatusMessageMap, - bodies: LanguageBodyMap -} - -export const FALLBACK_LANGUAGE = { - name: 'en' as const, - statusMessages: { - unableToSerializeResponse: 'Unable To Serialize Response', - unableToEncodeResponse: 'Unable To Encode Response', - unableToBindResourceDataSource: 'Unable To Bind $RESOURCE Data Source', - unableToInitializeResourceDataSource: 'Unable To Initialize $RESOURCE Data Source', - unableToFetchResourceCollection: 'Unable To Fetch $RESOURCE Collection', - unableToFetchResource: 'Unable To Fetch $RESOURCE', - unableToDeleteResource: 'Unable To Delete $RESOURCE', - languageNotAcceptable: 'Language Not Acceptable', - characterSetNotAcceptable: 'Character Set Not Acceptable', - unableToDeserializeResource: 'Unable To Deserialize $RESOURCE', - unableToDecodeResource: 'Unable To Decode $RESOURCE', - mediaTypeNotAcceptable: 'Media Type Not Acceptable', - methodNotAllowed: 'Method Not Allowed', - urlNotFound: 'URL Not Found', - badRequest: 'Bad Request', - ok: 'OK', - provideOptions: 'Provide Options', - resourceCollectionFetched: '$RESOURCE Collection Fetched', - resourceCollectionQueried: '$RESOURCE Collection Queried', - resourceFetched: '$RESOURCE Fetched', - resourceNotFound: '$RESOURCE Not Found', - deleteNonExistingResource: 'Delete Non-Existing $RESOURCE', - resourceDeleted: '$RESOURCE Deleted', - unableToDeserializeRequest: 'Unable To Deserialize Request', - patchNonExistingResource: 'Patch Non-Existing $RESOURCE', - unableToPatchResource: 'Unable To Patch $RESOURCE', - invalidResourcePatch: 'Invalid $RESOURCE Patch', - invalidResourcePatchType: 'Invalid $RESOURCE Patch Type', - invalidResource: 'Invalid $RESOURCE', - resourcePatched: '$RESOURCE Patched', - resourceCreated: '$RESOURCE Created', - resourceReplaced: '$RESOURCE Replaced', - unableToGenerateIdFromResourceDataSource: 'Unable To Generate ID From $RESOURCE Data Source', - unableToAssignIdFromResourceDataSource: 'Unable To Assign ID From $RESOURCE Data Source', - unableToEmplaceResource: 'Unable To Emplace $RESOURCE', - resourceIdNotGiven: '$RESOURCE ID Not Given', - unableToCreateResource: 'Unable To Create $RESOURCE', - notImplemented: 'Not Implemented', - internalServerError: 'Internal Server Error', - }, - bodies: { - badRequest: [ - 'An invalid request has been made.', - [ - 'Check if the request body has all the required attributes for this endpoint.', - 'Check if the request body has only the valid attributes for this endpoint.', - 'Check if the request body matches the schema for the resource associated with this endpoint.', - 'Check if the request is appropriate for this endpoint.', - ], - ], - languageNotAcceptable: [ - 'The server could not process a response suitable for the client\'s provided language requirement.', - [ - 'Choose from the available languages on this service.', - 'Contact the administrator to provide localization for the client\'s given requirements.', - ], - ], - characterSetNotAcceptable: [ - 'The server could not process a response suitable for the client\'s provided character set requirement.', - [ - 'Choose from the available character sets on this service.', - 'Contact the administrator to provide localization for the client\'s given requirements.', - ], - ], - mediaTypeNotAcceptable: [ - 'The server could not process a response suitable for the client\'s provided media type requirement.', - [ - 'Choose from the available media types on this service.', - 'Contact the administrator to provide localization for the client\'s given requirements.', - ], - ], - deleteNonExistingResource: [ - 'The client has attempted to delete a resource that does not exist.', - [ - 'Ensure that the resource still exists.', - 'Ensure that the correct method is provided.', - ], - ], - internalServerError: [ - 'An unknown error has occurred within the service.', - [ - 'Try the request again at a later time.', - 'Contact the administrator if the service remains in a degraded or non-functional state.', - ], - ], - invalidResource: [ - 'The request has an invalid structure or is missing some attributes.', - [ - 'Check if the request body has all the required attributes for this endpoint.', - 'Check if the request body has only the valid attributes for this endpoint.', - 'Check if the request body matches the schema for the resource associated with this endpoint.', - ], - ], - invalidResourcePatch: [ - 'The request has an invalid patch data.', - [ - 'Check if the appropriate patch type is specified on the request data.', - 'Check if the request body has all the required attributes for this endpoint.', - 'Check if the request body has only the valid attributes for this endpoint.', - 'Check if the request body matches the schema for the resource associated with this endpoint.', - ], - ], - invalidResourcePatchType: [ - 'The request has an invalid or unsupported kind of patch data.', - [ - 'Check if the appropriate patch type is specified on the request data.', - 'Check if the request body has all the required attributes for this endpoint.', - 'Check if the request body has only the valid attributes for this endpoint.', - 'Check if the request body matches the schema for the resource associated with this endpoint.', - ], - ], - methodNotAllowed: [ - 'A request with an invalid or unsupported method has been made.', - [ - 'Check if the request method is appropriate for this endpoint.', - 'Check if the client is authorized to perform the method on this endpoint.', - ] - ], - notImplemented: [ - 'The service does not have any implementation for the accessed endpoint.', - [ - 'Try the request again at a later time.', - 'Contact the administrator if the service remains in a degraded or non-functional state.', - ], - ], - patchNonExistingResource: [ - 'The client has attempted to patch a resource that does not exist.', - [ - 'Ensure that the resource still exists.', - 'Ensure that the correct method is provided.', - ], - ], - resourceIdNotGiven: [ - 'The resource ID is not provided for the accessed endpoint.', - [ - 'Check if the resource ID is provided and valid in the URL.', - 'Check if the request method is appropriate for this endpoint.', - ], - ], - unableToAssignIdFromResourceDataSource: [ - 'The resource could not be assigned an ID from the associated data source.', - [ - 'Try the request again at a later time.', - 'Contact the administrator regarding missing configuration or unavailability of dependencies.', - ], - ], - unableToBindResourceDataSource: [ - 'The resource could not be associated from the data source.', - [ - 'Try the request again at a later time.', - 'Contact the administrator regarding missing configuration or unavailability of dependencies.', - ], - ], - unableToCreateResource: [ - 'An error has occurred on creating the resource.', - [ - 'Check if the request method is appropriate for this endpoint.', - 'Check if the request body has all the required attributes for this endpoint.', - 'Check if the request body has only the valid attributes for this endpoint.', - 'Check if the request body matches the schema for the resource associated with this endpoint.', - 'Try the request again at a later time.', - 'Contact the administrator regarding missing configuration or unavailability of dependencies.', - ], - ], - unableToDecodeResource: [ - 'The resource byte array could not be decoded for the provided character set.', - [ - 'Choose from the available character sets on this service.', - 'Contact the administrator to provide localization for the client\'s given requirements.', - ], - ], - unableToDeleteResource: [ - 'An error has occurred on deleting the resource.', - [ - 'Check if the request method is appropriate for this endpoint.', - 'Try the request again at a later time.', - 'Contact the administrator regarding missing configuration or unavailability of dependencies.', - ], - ], - unableToDeserializeRequest: [ - 'The decoded request byte array could not be deserialized for the provided media type.', - [ - 'Choose from the available media types on this service.', - 'Contact the administrator to provide localization for the client\'s given requirements.', - ], - ], - unableToDeserializeResource: [ - 'The decoded resource could not be deserialized for the provided media type.', - [ - 'Choose from the available media types on this service.', - 'Contact the administrator to provide localization for the client\'s given requirements.', - ], - ], - unableToEmplaceResource: [ - 'An error has occurred on emplacing the resource.', - [ - 'Check if the request method is appropriate for this endpoint.', - 'Check if the request body has all the required attributes for this endpoint.', - 'Check if the request body has only the valid attributes for this endpoint.', - 'Check if the request body matches the schema for the resource associated with this endpoint.', - 'Try the request again at a later time.', - 'Contact the administrator regarding missing configuration or unavailability of dependencies.', - ], - ], - unableToEncodeResponse: [ - 'The response data could not be encoded for the provided character set.', - [ - 'Choose from the available character sets on this service.', - 'Contact the administrator to provide localization for the client\'s given requirements.', - ], - ], - unableToFetchResource: [ - 'An error has occurred on fetching the resource.', - [ - 'Check if the request method is appropriate for this endpoint.', - 'Try the request again at a later time.', - 'Contact the administrator regarding missing configuration or unavailability of dependencies.', - ], - ], - unableToFetchResourceCollection: [ - 'An error has occurred on fetching the resource collection.', - [ - 'Check if the request method is appropriate for this endpoint.', - 'Try the request again at a later time.', - 'Contact the administrator regarding missing configuration or unavailability of dependencies.', - ], - ], - unableToGenerateIdFromResourceDataSource: [ - 'The associated data source for the resource could not produce an ID.', - [ - 'Try the request again at a later time.', - 'Contact the administrator regarding missing configuration or unavailability of dependencies.', - ], - ], - unableToInitializeResourceDataSource: [ - 'The associated data source for the resource could not be connected for usage.', - [ - 'Try the request again at a later time.', - 'Contact the administrator regarding missing configuration or unavailability of dependencies.', - ], - ], - unableToPatchResource: [ - 'An error has occurred on patching the resource.', - [ - 'Check if the request method is appropriate for this endpoint.', - 'Check if the request body has all the required attributes for this endpoint.', - 'Check if the request body has only the valid attributes for this endpoint.', - 'Check if the request body matches the schema for the resource associated with this endpoint.', - 'Try the request again at a later time.', - 'Contact the administrator regarding missing configuration or unavailability of dependencies.', - ], - ], - unableToSerializeResponse: [ - 'The response data could not be serialized for the provided media type.', - [ - 'Choose from the available media types on this service.', - 'Contact the administrator to provide localization for the client\'s given requirements.', - ], - ], - urlNotFound: [ - 'An endpoint in the provided URL could not be found.', - [ - 'Check if the request URL is correct.', - 'Try the request again at a later time.', - 'Contact the administrator regarding missing configuration or unavailability of dependencies.', - ], - ], - resourceNotFound: [ - 'The resource in the provided URL could not be found.', - [ - 'Check if the request URL is correct.', - 'Try the request again at a later time.', - 'Contact the administrator regarding missing configuration or unavailability of dependencies.', - ], - ], - }, -} satisfies Language; diff --git a/packages/core/src/common/media-type.ts b/packages/core/src/common/media-type.ts deleted file mode 100644 index be412ae..0000000 --- a/packages/core/src/common/media-type.ts +++ /dev/null @@ -1,37 +0,0 @@ -export interface MediaType< - Name extends string = string, - T extends object = object, - SerializeOpts extends {} = {}, - DeserializeOpts extends {} = {} -> { - name: Name; - serialize: (object: T, args?: SerializeOpts) => string; - deserialize: (s: string, args?: DeserializeOpts) => T; -} - -export const FALLBACK_MEDIA_TYPE = { - serialize: (obj: unknown) => JSON.stringify(obj), - deserialize: (str: string) => JSON.parse(str), - name: 'application/json' as const, -} satisfies MediaType; - -export const PATCH_CONTENT_TYPES = [ - 'application/merge-patch+json', - 'application/json-patch+json', -] as const; - -export type PatchContentType = typeof PATCH_CONTENT_TYPES[number]; - -export const getAcceptPostString = (mediaTypes: Map) => Array.from(mediaTypes.keys()) - .filter((t) => !PATCH_CONTENT_TYPES.includes(t as PatchContentType)) - .join(','); - -export const isTextMediaType = (mediaType: string) => ( - mediaType.startsWith('text/') - || [ - 'application/json', - 'application/xml', - 'application/x-www-form-urlencoded', - ...PATCH_CONTENT_TYPES, - ].includes(mediaType) -); diff --git a/packages/core/src/common/queries/common.ts b/packages/core/src/common/queries/common.ts deleted file mode 100644 index fe462d9..0000000 --- a/packages/core/src/common/queries/common.ts +++ /dev/null @@ -1,53 +0,0 @@ -import {MediaType} from '../media-type'; - -const OPERATORS = [ - '=', - '!=', - '>=', - '<=', - '>', - '<', - 'LIKE', - 'ILIKE', - 'REGEXP', -] as const; - -type QueryOperator = typeof OPERATORS[number]; - -type QueryExpressionValue = string | number | boolean; - -interface QueryOperatorExpression { - lhs: string; - operator: QueryOperator; - rhs: QueryExpressionValue | RegExp; -} - -interface QueryFunctionExpression { - name: string; - args: QueryExpressionValue[]; -} - -export type QueryAnyExpression = QueryOperatorExpression | QueryFunctionExpression; - -export interface QueryOrGrouping { - type: 'or'; - expressions: QueryAnyExpression[]; -} - -export interface QueryAndGrouping { - type: 'and'; - expressions: QueryOrGrouping[]; -} - -export type Query = QueryAndGrouping; - -export interface QueryMediaType< - Name extends string = string, - SerializeOptions extends {} = {}, - DeserializeOptions extends {} = {} -> extends MediaType< - Name, - QueryAndGrouping, - SerializeOptions, - DeserializeOptions -> {} diff --git a/packages/core/src/common/queries/errors.ts b/packages/core/src/common/queries/errors.ts deleted file mode 100644 index b6eb7e5..0000000 --- a/packages/core/src/common/queries/errors.ts +++ /dev/null @@ -1,4 +0,0 @@ - -export class DeserializeError extends Error {} - -export class SerializeError extends Error {} diff --git a/packages/core/src/common/queries/index.ts b/packages/core/src/common/queries/index.ts deleted file mode 100644 index 3567d32..0000000 --- a/packages/core/src/common/queries/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './common'; -export * from './errors'; -export * as queryMediaTypes from './media-types'; diff --git a/packages/core/src/common/queries/media-types/application/x-www-form-urlencoded.ts b/packages/core/src/common/queries/media-types/application/x-www-form-urlencoded.ts deleted file mode 100644 index 984a576..0000000 --- a/packages/core/src/common/queries/media-types/application/x-www-form-urlencoded.ts +++ /dev/null @@ -1,248 +0,0 @@ -import { - QueryMediaType, - QueryAndGrouping, - QueryAnyExpression, - QueryOrGrouping, -} from '../../common'; - -import { - DeserializeError, - SerializeError, -} from '../../errors'; - -interface ProcessEntryBase { - type: string; -} - -const AVAILABLE_MATCH_TYPES = [ - 'startsWith', - 'endsWith', - 'includes', - 'regexp', -] as const; - -type MatchType = typeof AVAILABLE_MATCH_TYPES[number]; - -interface ProcessEntryString extends ProcessEntryBase { - type: 'string'; - matchType?: MatchType; - caseInsensitiveMatch?: boolean; -} - -interface ProcessEntryNumber extends ProcessEntryBase { - type: 'number'; - decimal?: boolean; -} - -interface ProcessEntryBoolean extends ProcessEntryBase { - type: 'boolean'; - truthyStrings?: string[]; -} - -type ProcessEntry = ProcessEntryString | ProcessEntryNumber | ProcessEntryBoolean; - -export const name = 'application/x-www-form-urlencoded' as const; - -class DeserializeInvalidFormatError extends DeserializeError {} - -const normalizeRhs = (lhs: string, rhs: string, processEntriesMap?: Record) => { - const defaultCoerceValues = { - type: 'string' - } as ProcessEntry; - const coerceValues = processEntriesMap?.[lhs] ?? defaultCoerceValues; - - if (coerceValues?.type === 'number') { - return { - lhs, - operator: '=', - rhs: Number(rhs) - } as QueryAnyExpression; - } - - if (coerceValues?.type === 'boolean') { - const truthyStrings = [ - ...(coerceValues.truthyStrings ?? []).map((s) => s.trim().toLowerCase()), - 'true', - ]; - - return { - lhs, - operator: '=', - rhs: truthyStrings.includes(rhs) - } as QueryAnyExpression; - } - - if (coerceValues?.type === 'string') { - switch (coerceValues?.matchType) { - case 'startsWith': { - return { - lhs, - operator: coerceValues.caseInsensitiveMatch ? 'ILIKE' : 'LIKE', - rhs: `%${rhs}`, - } as QueryAnyExpression; - } - case 'endsWith': { - return { - lhs, - operator: coerceValues.caseInsensitiveMatch ? 'ILIKE' : 'LIKE', - rhs: `${rhs}%`, - } as QueryAnyExpression; - } - case 'includes': { - return { - lhs, - operator: coerceValues.caseInsensitiveMatch ? 'ILIKE' : 'LIKE', - rhs: `%${rhs}%`, - } as QueryAnyExpression; - } - case 'regexp': { - return { - lhs, - operator: 'REGEXP', - rhs: new RegExp(rhs, coerceValues.caseInsensitiveMatch ? 'i' : ''), - } as QueryAnyExpression; - } - default: - break; - } - - return { - lhs, - operator: '=', - rhs, - } as QueryAnyExpression; - } - - const unknownCoerceValues = coerceValues as unknown as Record; - throw new DeserializeInvalidFormatError(`Invalid coercion type: ${unknownCoerceValues.type}`); - // this will be sent to the data source, e.g., the SQL query - // we can also make this function act as a "sanitizer" -} - -interface SerializeOptions {} - -interface DeserializeOptions { - processEntries?: Record; -} - -const doesGroupHaveExpression = (ex2: QueryAnyExpression, key: string) => { - if ('operator' in ex2) { - return ex2.lhs === key; - } - - if ('name' in ex2) { - return ex2.name === key; - } - - return false; -}; - -export const deserialize: QueryMediaType< - typeof name, - SerializeOptions, - DeserializeOptions ->['deserialize'] = (s: string, options = {} as DeserializeOptions) => { - const q = new URLSearchParams(s); - - return Array.from(q.entries()).reduce( - (queries, [key, value]) => { - const defaultOr = { - type: 'or', - expressions: [], - } as QueryOrGrouping; - const existingOr = queries.expressions.find((ex) => ( - ex.expressions.some((ex2) => doesGroupHaveExpression(ex2, key)) - )) ?? defaultOr; - const existingLhs = existingOr.expressions.find((ex) => doesGroupHaveExpression(ex, key)); - const newExpression = normalizeRhs(key, value, options.processEntries); - - if (typeof existingLhs === 'undefined') { - return { - ...queries, - expressions: [ - ...queries.expressions, - { - type: 'or', - expressions: [ - newExpression, - ], - }, - ], - }; - } - - return { - ...queries, - expressions: queries.expressions.map((ex) => ( - ex.expressions.some((ex2) => !doesGroupHaveExpression(ex2, key)) - ? ex - : { - ...existingOr, - expressions: [ - ...(existingOr.expressions ?? []), - newExpression, - ], - } - )), - }; - }, - { - type: 'and', - expressions: [], - } as QueryAndGrouping - ) -}; - -class SerializeInvalidExpressionError extends SerializeError {} - -const serializeExpression = (ex2: QueryAnyExpression) => { - if ('name' in ex2) { - return [ex2.name, `(${ex2.args.map((s) => s.toString()).join(',')})`]; - } - - if (ex2.rhs instanceof RegExp) { - if (ex2.operator !== 'REGEXP') { - throw new SerializeInvalidExpressionError(`Invalid rhs given for operator: ${ex2.lhs} ${ex2.operator} `); - } - - return [ex2.lhs, ex2.rhs.toString()]; - } - - switch (typeof ex2.rhs) { - case 'string': { - switch (ex2.operator) { - case 'ILIKE': - case 'LIKE': - return [ex2.lhs, ex2.rhs.replace(/^%+/, '').replace(/%+$/, '')]; - case '=': - return [ex2.lhs, ex2.rhs]; - default: - break; - } - throw new SerializeInvalidExpressionError(`Invalid operator given for lhs: ${ex2.lhs} ${ex2.rhs}`); - } - case 'number': { - return [ex2.lhs, ex2.rhs.toString()]; - } - case 'boolean': { - return [ex2.lhs, ex2.rhs ? 'true' : 'false']; - } - default: - break; - } - - throw new SerializeInvalidExpressionError(`Unknown type for rhs: ${ex2.lhs} ${ex2.operator} `); -}; - -export const serialize: QueryMediaType< - typeof name, - SerializeOptions, - DeserializeOptions ->['serialize'] = (q: QueryAndGrouping) => ( - new URLSearchParams( - q.expressions.flatMap((ex) => ( - ex.expressions.map((ex2) => serializeExpression(ex2)) - )) - ) - .toString() -); diff --git a/packages/core/src/common/queries/media-types/index.ts b/packages/core/src/common/queries/media-types/index.ts deleted file mode 100644 index 96c8ca8..0000000 --- a/packages/core/src/common/queries/media-types/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * as applicationXWwwFormUrlencoded from './application/x-www-form-urlencoded'; diff --git a/packages/core/src/common/resource.ts b/packages/core/src/common/resource.ts deleted file mode 100644 index 424aec4..0000000 --- a/packages/core/src/common/resource.ts +++ /dev/null @@ -1,205 +0,0 @@ -import * as v from 'valibot'; -import {PatchContentType} from './media-type'; -import {DataSource, ResourceIdConfig} from '../backend/data-source'; - -export const CAN_PATCH_VALID_VALUES = ['merge', 'delta'] as const; - -export type CanPatchSpec = typeof CAN_PATCH_VALID_VALUES[number]; - -export const PATCH_CONTENT_MAP_TYPE: Record = { - 'application/merge-patch+json': 'merge', - 'application/json-patch+json': 'delta', -}; - -type CanPatchObject = Record; - -export interface Relationship { - objectResource: Resource, - name: string; - // points to object ID - subjectAttr: string; -} - -export interface ResourceState< - ItemName extends string = string, - RouteName extends string = string -> { - shared: Map; - relationships: Map>; - itemName: ItemName; - routeName: RouteName; - canCreate: boolean; - canFetchCollection: boolean; - canFetchItem: boolean; - canPatch: CanPatchObject; - canEmplace: boolean; - canDelete: boolean; -} - -type CanPatch = boolean | Partial | CanPatchSpec[]; - -export interface BaseResourceType { - schema: v.BaseSchema; - name: string; - routeName: string; - idAttr: string; - idSchema: v.BaseSchema; - createdAtAttr: string; - updatedAtAttr: string; -} - -export interface Resource { - schema: ResourceType['schema']; - state: ResourceState; - name(n: NewName): Resource; - route(n: NewRouteName): Resource; - canFetchCollection(b?: boolean): this; - canFetchItem(b?: boolean): this; - canCreate(b?: boolean): this; - canPatch(b?: CanPatch): this; - canEmplace(b?: boolean): this; - canDelete(b?: boolean): this; - relatesTo( - resource: Resource, - relationshipParams: Relationship, - ): this; - dataSource?: DataSource; - id( - newIdAttr: NewIdAttr, - params: ResourceIdConfig - ): Resource; - addMetadata(id: string, value: unknown): this; - setMetadata(id: string, value: unknown): this; - createdAt(n: NewCreatedAtAttr): Resource; - updatedAt(n: NewUpdatedAtAttr): Resource; -} - -export const resource = (schema: ResourceType['schema']): Resource => { - const resourceState = { - shared: new Map(), - relationships: new Map>(), - canCreate: false, - canFetchCollection: false, - canFetchItem: false, - canPatch: { - merge: false, - delta: false, - }, - canEmplace: false, - canDelete: false, - } as ResourceState; - - return { - get state(): ResourceState { - return Object.freeze({ - ...resourceState, - }); - }, - canFetchCollection(b = true) { - resourceState.canFetchCollection = b; - return this; - }, - canFetchItem(b = true) { - resourceState.canFetchItem = b; - return this; - }, - canCreate(b = true) { - resourceState.canCreate = b; - return this; - }, - canPatch(b = true as CanPatch) { - if (typeof b === 'boolean') { - resourceState.canPatch.merge = b; - resourceState.canPatch.delta = b; - return this; - } - - if (typeof b === 'object') { - if (Array.isArray(b)) { - CAN_PATCH_VALID_VALUES.forEach((p) => { - resourceState.canPatch[p] = b.includes(p); - }); - return this; - } - if (b !== null) { - CAN_PATCH_VALID_VALUES.forEach((p) => { - resourceState.canPatch[p] = b[p] ?? false; - }); - } - } - - return this; - }, - canEmplace(b = true) { - resourceState.canEmplace = b; - return this; - }, - canDelete(b = true) { - resourceState.canDelete = b; - return this; - }, - id(idName, config) { - resourceState.shared.set('idAttr', idName); - resourceState.shared.set('idConfig', config); - return this; - }, - addMetadata(key: string, value: unknown) { - const fullTextAttrs = (resourceState.shared.get(key) ?? new Set()) as Set; - fullTextAttrs.add(value); - this.setMetadata(key, fullTextAttrs); - return this; - }, - setMetadata(key: string, value: unknown) { - resourceState.shared.set(key, value); - return this; - }, - name(n: NewName) { - resourceState.itemName = n; - return this; - }, - route(n: NewRouteName) { - resourceState.routeName = n; - return this; - }, - get itemName() { - return resourceState.itemName; - }, - get routeName() { - return resourceState.routeName; - }, - get schema() { - return schema; - }, - relatesTo( - objectResource: Resource, - relationshipParams: Relationship, - ) { - resourceState.relationships.set(relationshipParams.name, { - ...relationshipParams, - objectResource, - }); - return this; - }, - createdAt(n: NewCreatedAtAttr) { - resourceState.shared.set('createdAtAttr', n); - return this; - }, - updatedAt(n: NewUpdatedAtAttr) { - resourceState.shared.set('updatedAtAttr', n); - return this; - }, - } as Resource; -}; - -export type ResourceType = v.Output; - -export const getAcceptPatchString = (canPatch: CanPatchObject) => { - const validPatchTypes = Object.entries(canPatch) - .filter(([, allowed]) => allowed) - .map(([patchType]) => patchType); - - return Object.entries(PATCH_CONTENT_MAP_TYPE) - .filter(([, patchType]) => validPatchTypes.includes(patchType)) - .map(([contentType ]) => contentType) - .join(','); -} diff --git a/packages/core/src/common/validation.ts b/packages/core/src/common/validation.ts deleted file mode 100644 index eba4b29..0000000 --- a/packages/core/src/common/validation.ts +++ /dev/null @@ -1 +0,0 @@ -export * from 'valibot'; diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts new file mode 100644 index 0000000..a9b27a6 --- /dev/null +++ b/packages/core/src/index.ts @@ -0,0 +1,62 @@ +import {app, endpoint, operation, validation as v} from './common/app'; + +const theEndpoint = endpoint({ + schema: v.object({ + username: v.string(), + }), +}) + .can('patch') + .can('query'); + +const canPatch = operation({ + name: 'patch' as const, + args: [ + 'merge', + 'delta', + ] as const, + // TODO define resource-specific stuff, like defining URL params, etc. +}); + +const canFetch = operation({ + name: 'fetch' as const, + args: [ + 'item', + 'collection', + ] as const, +}); + +const canQuery = operation({ + name: 'query' as const, +}); + +const canCreate = operation({ + name: 'create' as const, +}); + +const canEmplace = operation({ + name: 'emplace' as const, +}); + +const canDelete = operation({ + name: 'delete' as const, +}); + +export const theApp = app({ + name: 'foo' as const, +}) + .operation(canQuery) + .operation(canPatch) + .operation(canFetch) + .operation(canCreate) + .operation(canEmplace) + .operation(canDelete) + .endpoint(theEndpoint); +// +// const bootstrap = async (theApp: App) => { +// if (typeof window === 'undefined') { +// const { backend } = await import('./backend'); +// const theBackend = backend({ +// app: theApp +// }); +// } +// }; diff --git a/packages/core/test/features/query.test.ts b/packages/core/test/features/query.test.ts deleted file mode 100644 index 76abd55..0000000 --- a/packages/core/test/features/query.test.ts +++ /dev/null @@ -1,271 +0,0 @@ -import {describe, expect, it} from 'vitest'; -import { queryMediaTypes } from '../../src/common'; - -describe('query', () => { - it('returns a data source query collection from search params', () => { - const q = new URLSearchParams(); - const collection = queryMediaTypes.applicationXWwwFormUrlencoded.deserialize(q.toString()); - expect(collection.expressions).toBeInstanceOf(Array); - }); - - it('coerces a numeric value', () => { - const q = new URLSearchParams({ - attr: '2', - attr2: '2', - }); - const collection = queryMediaTypes.applicationXWwwFormUrlencoded.deserialize( - q.toString(), - { - processEntries: { - attr: { - type: 'number' - } - }, - }, - ); - expect(collection).toEqual({ - type: 'and', - expressions: [ - { - type: 'or', - expressions: [ - { - lhs: 'attr', - operator: '=', - rhs: 2, - }, - ], - }, - { - type: 'or', - expressions: [ - { - lhs: 'attr2', - operator: '=', - rhs: '2', - }, - ], - }, - ], - }); - }); - - it('coerces a boolean value', () => { - const q = new URLSearchParams({ - attr: 'true', - attr2: 'false', - attr3: 'true', - attr4: 'false', - }); - const collection = queryMediaTypes.applicationXWwwFormUrlencoded.deserialize( - q.toString(), - { - processEntries: { - attr: { - type: 'boolean', - }, - attr2: { - type: 'boolean', - } - }, - }, - ); - expect(collection).toEqual({ - type: 'and', - expressions: [ - { - type: 'or', - expressions: [ - { - lhs: 'attr', - operator: '=', - rhs: true, - }, - ], - }, - { - type: 'or', - expressions: [ - { - lhs: 'attr2', - operator: '=', - rhs: false, - }, - ], - }, - { - type: 'or', - expressions: [ - { - lhs: 'attr3', - operator: '=', - rhs: 'true', - }, - ], - }, - { - type: 'or', - expressions: [ - { - lhs: 'attr4', - operator: '=', - rhs: 'false', - }, - ], - }, - ], - }); - }); - - it('returns an equal query', () => { - const q = new URLSearchParams({ - attr: 'foo' - }); - const collection = queryMediaTypes.applicationXWwwFormUrlencoded.deserialize( - q.toString() - ); - expect(collection).toEqual({ - type: 'and', - expressions: [ - { - type: 'or', - expressions: [ - { - lhs: 'attr', - operator: '=', - rhs: 'foo', - }, - ], - }, - ], - }); - }); - - it('returns an AND operator query', () => { - const q = new URLSearchParams([ - ['attr', 'foo'], - ['attr2', 'bar'], - ]); - const collection = queryMediaTypes.applicationXWwwFormUrlencoded.deserialize( - q.toString() - ); - expect(collection).toEqual({ - type: 'and', - expressions: [ - { - type: 'or', - expressions: [ - { - lhs: 'attr', - operator: '=', - rhs: 'foo', - }, - ], - }, - { - type: 'or', - expressions: [ - { - lhs: 'attr2', - operator: '=', - rhs: 'bar', - }, - ], - }, - ], - }); - }); - - it('returns an OR operator query', () => { - const q = new URLSearchParams([ - ['attr', 'foo'], - ['attr', 'bar'], - ]); - const collection = queryMediaTypes.applicationXWwwFormUrlencoded.deserialize( - q.toString() - ); - expect(collection).toEqual({ - type: 'and', - expressions: [ - { - type: 'or', - expressions: [ - { - lhs: 'attr', - operator: '=', - rhs: 'foo', - }, - { - lhs: 'attr', - operator: '=', - rhs: 'bar', - } - ] - }, - ], - }); - }); - - it('returns an query with appropriate grouping', () => { - const q = new URLSearchParams([ - ['attr3', 'quux'], - ['attr', 'foo'], - ['attr4', 'quuux'], - ['attr', 'bar'], - ['attr2', 'baz'], - ]); - const collection = queryMediaTypes.applicationXWwwFormUrlencoded.deserialize( - q.toString() - ); - expect(collection).toEqual({ - type: 'and', - expressions: [ - { - type: 'or', - expressions: [ - { - lhs: 'attr3', - operator: '=', - rhs: 'quux', - }, - ], - }, - { - type: 'or', - expressions: [ - { - lhs: 'attr', - operator: '=', - rhs: 'foo', - }, - { - lhs: 'attr', - operator: '=', - rhs: 'bar', - } - ] - }, - { - type: 'or', - expressions: [ - { - lhs: 'attr4', - operator: '=', - rhs: 'quuux', - }, - ], - }, - { - type: 'or', - expressions: [ - { - lhs: 'attr2', - operator: '=', - rhs: 'baz', - }, - ], - }, - ], - }); - }); -}); diff --git a/packages/data-sources/duckdb/test/index.test.ts b/packages/core/test/index.test.ts similarity index 100% rename from packages/data-sources/duckdb/test/index.test.ts rename to packages/core/test/index.test.ts diff --git a/packages/core/test/utils.ts b/packages/core/test/utils.ts deleted file mode 100644 index 3468477..0000000 --- a/packages/core/test/utils.ts +++ /dev/null @@ -1,480 +0,0 @@ -import {IncomingHttpHeaders, IncomingMessage, OutgoingHttpHeaders, request, RequestOptions} from 'http'; -import {Method, DataSource} from '../src/backend'; -import {FALLBACK_LANGUAGE, Language} from '../src/common'; - -interface ClientParams { - method: Method; - path: string; - headers?: IncomingHttpHeaders; - body?: unknown; -} - -type ResponseBody = Buffer | string | object; - -export interface TestClient { - (params: ClientParams): Promise<[IncomingMessage, ResponseBody?]>; - acceptMediaType(mediaType: string): this; - acceptLanguage(language: string): this; - acceptCharset(charset: string): this; - contentType(mediaType: string): this; - contentCharset(charset: string): this; -} - -export const createTestClient = (options: Omit): TestClient => { - const additionalHeaders: OutgoingHttpHeaders = {}; - const client = (params: ClientParams) => new Promise<[IncomingMessage, ResponseBody?]>((resolve, reject) => { - const { - ...etcAdditionalHeaders - } = additionalHeaders; - - // odd that request() uses OutgoingHttpHeaders instead of IncomingHttpHeaders... - const headers: OutgoingHttpHeaders = { - ...(options.headers ?? {}), - ...etcAdditionalHeaders, - ...(params.headers ?? {}), - }; - - let contentTypeHeader: string | undefined; - if (typeof params.body !== 'undefined') { - contentTypeHeader = headers['content-type'] = params.headers?.['content-type'] ?? 'application/json'; - } - - const req = request({ - ...options, - method: params.method, - path: params.path, - headers, - }); - - req.on('response', (res) => { - // if (req.method.toUpperCase() === 'QUERY') { - // res.statusMessage = ''; - // res.statusCode = 200; - // } - - res.on('error', (err) => { - reject(err); - }); - - let resBuffer: Buffer | undefined; - res.on('data', (c) => { - resBuffer = ( - typeof resBuffer === 'undefined' - ? Buffer.from(c) - : Buffer.concat([resBuffer, c]) - ); - }); - - res.on('close', () => { - const acceptHeader = Array.isArray(headers['accept']) ? headers['accept'].join('; ') : headers['accept']; - const contentTypeBase = acceptHeader ?? 'application/octet-stream'; - const [type, subtype] = contentTypeBase.split('/'); - const allSubtypes = subtype.split('+'); - if (typeof resBuffer !== 'undefined') { - if (allSubtypes.includes('json')) { - const acceptCharset = ( - Array.isArray(headers['accept-charset']) - ? headers['accept-charset'].join('; ') - : headers['accept-charset'] - ) as BufferEncoding | undefined; - resolve([res, JSON.parse(resBuffer.toString(acceptCharset ?? 'utf-8'))]); - return; - } - - if (type === 'text') { - const acceptCharset = ( - Array.isArray(headers['accept-charset']) - ? headers['accept-charset'].join('; ') - : headers['accept-charset'] - ) as BufferEncoding | undefined; - resolve([res, resBuffer.toString(acceptCharset ?? 'utf-8')]); - return; - } - - resolve([res, resBuffer]); - return; - } - - resolve([res]); - }); - }); - - req.on('error', (err) => { - reject(err); - }) - - if (typeof params.body !== 'undefined') { - const theContentTypeHeader = Array.isArray(contentTypeHeader) ? contentTypeHeader.join('; ') : contentTypeHeader?.toString(); - const contentTypeAll = theContentTypeHeader ?? 'application/octet-stream'; - const [contentTypeBase, ...contentTypeParams] = contentTypeAll.split(';').map((s) => s.replace(/\s+/g, '').trim()); - const charsetParam = contentTypeParams.find((s) => s.startsWith('charset=')); - const charset = charsetParam?.split('=')?.[1] as BufferEncoding | undefined; - const [, subtype] = contentTypeBase.split('/'); - const allSubtypes = subtype.split('+'); - req.write( - allSubtypes.includes('json') - ? JSON.stringify(params.body) - : Buffer.from(params.body?.toString() ?? '', contentTypeBase === 'text' ? charset : undefined) - ); - } - - req.end(); - }); - - client.acceptMediaType = function acceptMediaType(mediaType: string) { - additionalHeaders['accept'] = mediaType; - return this; - }; - - client.acceptLanguage = function acceptLanguage(language: string) { - additionalHeaders['accept-language'] = language; - return this; - }; - - client.acceptCharset = function acceptCharset(charset: string) { - additionalHeaders['accept-charset'] = charset; - return this; - }; - - client.contentType = function contentType(mediaType: string) { - additionalHeaders['content-type'] = mediaType; - return this; - }; - - client.contentCharset = function contentCharset(charset: string) { - additionalHeaders['content-type'] = `${additionalHeaders['content-type']}; charset="${charset}"`; - return this; - }; - - return client; -}; - -export const dummyGenerationStrategy = () => Promise.resolve(); - -export class DummyError extends Error {} - -export class DummyDataSource implements DataSource { - private resource?: { dataSource?: unknown }; - - async create(): Promise { - return {}; - } - - async delete(): Promise {} - - async emplace(): Promise<[object, boolean]> { - return [{}, false]; - } - - async getById(): Promise { - return {}; - } - - async newId(): Promise { - return ''; - } - - async getMultiple(): Promise { - return []; - } - - async getSingle(): Promise { - return {}; - } - - async getTotalCount(): Promise { - return 0; - } - - async initialize(): Promise {} - - async patch(): Promise { - return {}; - } - - prepareResource(rr: unknown) { - this.resource = rr as unknown as { dataSource: DummyDataSource }; - this.resource.dataSource = this; - } -} - -export const TEST_LANGUAGE: Language = { - name: FALLBACK_LANGUAGE.name, - statusMessages: { - resourceCollectionQueried: '$Resource Collection Queried', - unableToSerializeResponse: 'Unable To Serialize Response', - unableToEncodeResponse: 'Unable To Encode Response', - unableToBindResourceDataSource: 'Unable To Bind $RESOURCE Data Source', - unableToInitializeResourceDataSource: 'Unable To Initialize $RESOURCE Data Source', - unableToFetchResourceCollection: 'Unable To Fetch $RESOURCE Collection', - unableToFetchResource: 'Unable To Fetch $RESOURCE', - unableToDeleteResource: 'Unable To Delete $RESOURCE', - languageNotAcceptable: 'Language Not Acceptable', - characterSetNotAcceptable: 'Character Set Not Acceptable', - unableToDeserializeResource: 'Unable To Deserialize $RESOURCE', - unableToDecodeResource: 'Unable To Decode $RESOURCE', - mediaTypeNotAcceptable: 'Media Type Not Acceptable', - methodNotAllowed: 'Method Not Allowed', - urlNotFound: 'URL Not Found', - badRequest: 'Bad Request', - ok: 'OK', - provideOptions: 'Provide Options', - resourceCollectionFetched: '$RESOURCE Collection Fetched', - resourceFetched: '$RESOURCE Fetched', - resourceNotFound: '$RESOURCE Not Found', - deleteNonExistingResource: 'Delete Non-Existing $RESOURCE', - resourceDeleted: '$RESOURCE Deleted', - unableToDeserializeRequest: 'Unable To Deserialize Request', - patchNonExistingResource: 'Patch Non-Existing $RESOURCE', - unableToPatchResource: 'Unable To Patch $RESOURCE', - invalidResourcePatch: 'Invalid $RESOURCE Patch', - invalidResourcePatchType: 'Invalid $RESOURCE Patch Type', - invalidResource: 'Invalid $RESOURCE', - resourcePatched: '$RESOURCE Patched', - resourceCreated: '$RESOURCE Created', - resourceReplaced: '$RESOURCE Replaced', - unableToGenerateIdFromResourceDataSource: 'Unable To Generate ID From $RESOURCE Data Source', - unableToAssignIdFromResourceDataSource: 'Unable To Assign ID From $RESOURCE Data Source', - unableToEmplaceResource: 'Unable To Emplace $RESOURCE', - resourceIdNotGiven: '$RESOURCE ID Not Given', - unableToCreateResource: 'Unable To Create $RESOURCE', - notImplemented: 'Not Implemented', - internalServerError: 'Internal Server Error', - }, - bodies: { - badRequest: [ - 'An invalid request has been made.', - [ - 'Check if the request body has all the required attributes for this endpoint.', - 'Check if the request body has only the valid attributes for this endpoint.', - 'Check if the request body matches the schema for the resource associated with this endpoint.', - 'Check if the request is appropriate for this endpoint.', - ], - ], - languageNotAcceptable: [ - 'The server could not process a response suitable for the client\'s provided language requirement.', - [ - 'Choose from the available languages on this service.', - 'Contact the administrator to provide localization for the client\'s given requirements.', - ], - ], - characterSetNotAcceptable: [ - 'The server could not process a response suitable for the client\'s provided character set requirement.', - [ - 'Choose from the available character sets on this service.', - 'Contact the administrator to provide localization for the client\'s given requirements.', - ], - ], - mediaTypeNotAcceptable: [ - 'The server could not process a response suitable for the client\'s provided media type requirement.', - [ - 'Choose from the available media types on this service.', - 'Contact the administrator to provide localization for the client\'s given requirements.', - ], - ], - deleteNonExistingResource: [ - 'The client has attempted to delete a resource that does not exist.', - [ - 'Ensure that the resource still exists.', - 'Ensure that the correct method is provided.', - ], - ], - internalServerError: [ - 'An unknown error has occurred within the service.', - [ - 'Try the request again at a later time.', - 'Contact the administrator if the service remains in a degraded or non-functional state.', - ], - ], - invalidResource: [ - 'The request has an invalid structure or is missing some attributes.', - [ - 'Check if the request body has all the required attributes for this endpoint.', - 'Check if the request body has only the valid attributes for this endpoint.', - 'Check if the request body matches the schema for the resource associated with this endpoint.', - ], - ], - invalidResourcePatch: [ - 'The request has an invalid patch data.', - [ - 'Check if the appropriate patch type is specified on the request data.', - 'Check if the request body has all the required attributes for this endpoint.', - 'Check if the request body has only the valid attributes for this endpoint.', - 'Check if the request body matches the schema for the resource associated with this endpoint.', - ], - ], - invalidResourcePatchType: [ - 'The request has an invalid or unsupported kind of patch data.', - [ - 'Check if the appropriate patch type is specified on the request data.', - 'Check if the request body has all the required attributes for this endpoint.', - 'Check if the request body has only the valid attributes for this endpoint.', - 'Check if the request body matches the schema for the resource associated with this endpoint.', - ], - ], - methodNotAllowed: [ - 'A request with an invalid or unsupported method has been made.', - [ - 'Check if the request method is appropriate for this endpoint.', - 'Check if the client is authorized to perform the method on this endpoint.', - ] - ], - notImplemented: [ - 'The service does not have any implementation for the accessed endpoint.', - [ - 'Try the request again at a later time.', - 'Contact the administrator if the service remains in a degraded or non-functional state.', - ], - ], - patchNonExistingResource: [ - 'The client has attempted to patch a resource that does not exist.', - [ - 'Ensure that the resource still exists.', - 'Ensure that the correct method is provided.', - ], - ], - resourceIdNotGiven: [ - 'The resource ID is not provided for the accessed endpoint.', - [ - 'Check if the resource ID is provided and valid in the URL.', - 'Check if the request method is appropriate for this endpoint.', - ], - ], - unableToAssignIdFromResourceDataSource: [ - 'The resource could not be assigned an ID from the associated data source.', - [ - 'Try the request again at a later time.', - 'Contact the administrator regarding missing configuration or unavailability of dependencies.', - ], - ], - unableToBindResourceDataSource: [ - 'The resource could not be associated from the data source.', - [ - 'Try the request again at a later time.', - 'Contact the administrator regarding missing configuration or unavailability of dependencies.', - ], - ], - unableToCreateResource: [ - 'An error has occurred on creating the resource.', - [ - 'Check if the request method is appropriate for this endpoint.', - 'Check if the request body has all the required attributes for this endpoint.', - 'Check if the request body has only the valid attributes for this endpoint.', - 'Check if the request body matches the schema for the resource associated with this endpoint.', - 'Try the request again at a later time.', - 'Contact the administrator regarding missing configuration or unavailability of dependencies.', - ], - ], - unableToDecodeResource: [ - 'The resource byte array could not be decoded for the provided character set.', - [ - 'Choose from the available character sets on this service.', - 'Contact the administrator to provide localization for the client\'s given requirements.', - ], - ], - unableToDeleteResource: [ - 'An error has occurred on deleting the resource.', - [ - 'Check if the request method is appropriate for this endpoint.', - 'Try the request again at a later time.', - 'Contact the administrator regarding missing configuration or unavailability of dependencies.', - ], - ], - unableToDeserializeRequest: [ - 'The decoded request byte array could not be deserialized for the provided media type.', - [ - 'Choose from the available media types on this service.', - 'Contact the administrator to provide localization for the client\'s given requirements.', - ], - ], - unableToDeserializeResource: [ - 'The decoded resource could not be deserialized for the provided media type.', - [ - 'Choose from the available media types on this service.', - 'Contact the administrator to provide localization for the client\'s given requirements.', - ], - ], - unableToEmplaceResource: [ - 'An error has occurred on emplacing the resource.', - [ - 'Check if the request method is appropriate for this endpoint.', - 'Check if the request body has all the required attributes for this endpoint.', - 'Check if the request body has only the valid attributes for this endpoint.', - 'Check if the request body matches the schema for the resource associated with this endpoint.', - 'Try the request again at a later time.', - 'Contact the administrator regarding missing configuration or unavailability of dependencies.', - ], - ], - unableToEncodeResponse: [ - 'The response data could not be encoded for the provided character set.', - [ - 'Choose from the available character sets on this service.', - 'Contact the administrator to provide localization for the client\'s given requirements.', - ], - ], - unableToFetchResource: [ - 'An error has occurred on fetching the resource.', - [ - 'Check if the request method is appropriate for this endpoint.', - 'Try the request again at a later time.', - 'Contact the administrator regarding missing configuration or unavailability of dependencies.', - ], - ], - unableToFetchResourceCollection: [ - 'An error has occurred on fetching the resource collection.', - [ - 'Check if the request method is appropriate for this endpoint.', - 'Try the request again at a later time.', - 'Contact the administrator regarding missing configuration or unavailability of dependencies.', - ], - ], - unableToGenerateIdFromResourceDataSource: [ - 'The associated data source for the resource could not produce an ID.', - [ - 'Try the request again at a later time.', - 'Contact the administrator regarding missing configuration or unavailability of dependencies.', - ], - ], - unableToInitializeResourceDataSource: [ - 'The associated data source for the resource could not be connected for usage.', - [ - 'Try the request again at a later time.', - 'Contact the administrator regarding missing configuration or unavailability of dependencies.', - ], - ], - unableToPatchResource: [ - 'An error has occurred on patching the resource.', - [ - 'Check if the request method is appropriate for this endpoint.', - 'Check if the request body has all the required attributes for this endpoint.', - 'Check if the request body has only the valid attributes for this endpoint.', - 'Check if the request body matches the schema for the resource associated with this endpoint.', - 'Try the request again at a later time.', - 'Contact the administrator regarding missing configuration or unavailability of dependencies.', - ], - ], - unableToSerializeResponse: [ - 'The response data could not be serialized for the provided media type.', - [ - 'Choose from the available media types on this service.', - 'Contact the administrator to provide localization for the client\'s given requirements.', - ], - ], - urlNotFound: [ - 'An endpoint in the provided URL could not be found.', - [ - 'Check if the request URL is correct.', - 'Try the request again at a later time.', - 'Contact the administrator regarding missing configuration or unavailability of dependencies.', - ], - ], - resourceNotFound: [ - 'The resource in the provided URL could not be found.', - [ - 'Check if the request URL is correct.', - 'Try the request again at a later time.', - 'Contact the administrator regarding missing configuration or unavailability of dependencies.', - ], - ], - }, -}; diff --git a/packages/core/tsconfig.json b/packages/core/tsconfig.json index 7980d5f..74083d7 100644 --- a/packages/core/tsconfig.json +++ b/packages/core/tsconfig.json @@ -3,10 +3,7 @@ "include": ["src", "types"], "compilerOptions": { "module": "ESNext", - "lib": [ - "ESNext", - "dom" - ], + "lib": ["ESNext"], "importHelpers": true, "declaration": true, "sourceMap": true, @@ -21,8 +18,6 @@ "esModuleInterop": true, "target": "es2018", "useDefineForClassFields": false, - "declarationMap": true, - "experimentalDecorators": true, - "emitDecoratorMetadata": true + "declarationMap": true } } diff --git a/packages/data-sources/duckdb/LICENSE b/packages/data-sources/duckdb/LICENSE deleted file mode 100644 index 5a4fdfd..0000000 --- a/packages/data-sources/duckdb/LICENSE +++ /dev/null @@ -1,7 +0,0 @@ -MIT License Copyright (c) 2024 TheoryOfNekomata - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/packages/data-sources/duckdb/package.json b/packages/data-sources/duckdb/package.json deleted file mode 100644 index 8f62054..0000000 --- a/packages/data-sources/duckdb/package.json +++ /dev/null @@ -1,67 +0,0 @@ -{ - "name": "@modal-sh/yasumi-data-source-duckdb", - "version": "0.0.0", - "files": [ - "dist", - "src" - ], - "engines": { - "node": ">=16" - }, - "license": "MIT", - "keywords": [ - "pridepack" - ], - "devDependencies": { - "@types/node": "^20.11.0", - "pridepack": "2.6.0", - "tslib": "^2.6.2", - "typescript": "^5.3.3", - "vitest": "^1.2.0" - }, - "dependencies": { - "@modal-sh/yasumi": "workspace:*", - "duckdb-async": "^0.10.0" - }, - "scripts": { - "prepublishOnly": "pridepack clean && pridepack build", - "build": "pridepack build", - "type-check": "pridepack check", - "clean": "pridepack clean", - "watch": "pridepack watch", - "start": "pridepack start", - "dev": "pridepack dev", - "test": "vitest" - }, - "private": false, - "description": "DuckDB adapter for Yasumi.", - "repository": { - "url": "", - "type": "git" - }, - "homepage": "", - "bugs": { - "url": "" - }, - "author": "TheoryOfNekomata ", - "publishConfig": { - "access": "public" - }, - "types": "./dist/types/index.d.ts", - "main": "./dist/cjs/production/index.js", - "module": "./dist/esm/production/index.js", - "exports": { - ".": { - "development": { - "require": "./dist/cjs/development/index.js", - "import": "./dist/esm/development/index.js" - }, - "require": "./dist/cjs/production/index.js", - "import": "./dist/esm/production/index.js", - "types": "./dist/types/index.d.ts" - } - }, - "typesVersions": { - "*": {} - } -} diff --git a/packages/data-sources/duckdb/pridepack.json b/packages/data-sources/duckdb/pridepack.json deleted file mode 100644 index 0bc7a8f..0000000 --- a/packages/data-sources/duckdb/pridepack.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "target": "es2018" -} diff --git a/packages/data-sources/duckdb/src/index.ts b/packages/data-sources/duckdb/src/index.ts deleted file mode 100644 index a25fba7..0000000 --- a/packages/data-sources/duckdb/src/index.ts +++ /dev/null @@ -1,268 +0,0 @@ -import { Resource, validation as v, BaseResourceType } from '@modal-sh/yasumi'; -import { DataSource, ResourceIdConfig } from '@modal-sh/yasumi/backend'; -import { Database } from 'duckdb-async'; -import assert from 'assert'; - -type ID = number; - -interface DuckDbDataSourceBase< - ID, - Schema extends v.BaseSchema = v.BaseSchema, - CurrentName extends string = string, - CurrentRouteName extends string = string, - Data extends object = v.Output, -> extends DataSource { - resource?: Resource; - - db?: Database; -} - -export const AutoincrementIdConfig = { - // TODO add options: https://duckdb.org/docs/sql/statements/create_sequence - generationStrategy: async (dataSourceRaw: DataSource) => { - const dataSource = dataSourceRaw as DuckDbDataSourceBase; - assert(typeof dataSource.db !== 'undefined'); - assert(typeof dataSource.resource !== 'undefined'); - const idAttr = dataSource.resource.state.shared.get('idAttr'); - assert(typeof idAttr === 'string'); - - const con = await dataSource.db.connect(); - const stmt = await con.prepare(` - SELECT nextval('${dataSource.resource.state.routeName}_sequence') as ${idAttr}; - `); - const [v] = await stmt.all(); - return v[idAttr]; - }, - schema: v.number(), - serialize: (v: unknown) => v?.toString() ?? '', - deserialize: (v: string) => Number(v), -} - -export class DuckDbDataSource< - Schema extends v.BaseSchema = v.BaseSchema, - CurrentName extends string = string, - CurrentRouteName extends string = string, - Data extends object = v.Output, -> implements DuckDbDataSourceBase { - resource?: Resource; - - db?: Database; - - constructor(private readonly path: string) { - // noop - } - - async initialize() { - assert(typeof this.path !== 'undefined'); - assert(typeof this.resource !== 'undefined'); - - const idConfig = this.resource.state.shared.get('idConfig') as ResourceIdConfig | undefined; - assert(typeof idConfig !== 'undefined'); - - const idAttr = this.resource.state.shared.get('idAttr'); - assert(typeof idAttr === 'string'); - - const idSchema = idConfig.schema as v.BaseSchema; - - this.db = await Database.create(this.path); - const clause = `CREATE TABLE IF NOT EXISTS ${this.resource.state.routeName}`; - const resourceSchema = this.resource.schema as unknown as v.ObjectSchema; - const tableSchema = Object.entries(resourceSchema.entries) - .map(([columnName, columnDefRaw]) => { - const columnDef = columnDefRaw as unknown as v.BaseSchema; - return [columnName, columnDef.type].join(' '); - }) - .join(','); - let sequenceSql = ''; - let defaultValue = ''; - let idType = 'STRING'; - if (idSchema.type === 'number') { - // TODO support more sequence statements: https://duckdb.org/docs/sql/statements/create_sequence - sequenceSql = `CREATE SEQUENCE IF NOT EXISTS ${this.resource.state.routeName}_sequence START 1;`; - defaultValue = `DEFAULT nextval('${this.resource.state.routeName}_sequence')`; - idType = 'INTEGER'; - } - const sql = `${sequenceSql}${clause} (${idAttr} ${idType} ${defaultValue},${tableSchema});`; - const con = await this.db.connect(); - const stmt = await con.prepare(sql); - await stmt.run(); - } - - prepareResource(resource: Resource) { - resource.dataSource = resource.dataSource ?? this; - const originalResourceId = resource.id; - resource.id = (newIdAttr: NewIdAttr, params: ResourceIdConfig) => { - originalResourceId(newIdAttr, params); - return resource as Resource; - }; - this.resource = resource as any; - } - - async getMultiple(query) { - // TODO translate query to SQL statements - assert(typeof this.db !== 'undefined'); - assert(typeof this.resource !== 'undefined'); - - const con = await this.db.connect(); - const stmt = await con.prepare(` - SELECT * FROM ${this.resource.state.routeName}; - `); - const data = await stmt.all(); - return data as Data[]; - } - - async newId() { - assert(typeof this.resource !== 'undefined'); - const idConfig = this.resource.state.shared.get('idConfig') as ResourceIdConfig; - assert(typeof idConfig !== 'undefined'); - - const theNewId = await idConfig.generationStrategy(this); - return theNewId as ID; - } - - async create(data: Data) { - assert(typeof this.db !== 'undefined'); - assert(typeof this.resource !== 'undefined'); - - const idConfig = this.resource.state.shared.get('idConfig') as ResourceIdConfig | undefined; - assert(typeof idConfig !== 'undefined'); - - const idAttr = this.resource.state.shared.get('idAttr'); - assert(typeof idAttr === 'string'); - - let theId: any; - const { [idAttr]: dataId } = data as Record; - if (typeof dataId !== 'undefined') { - theId = idConfig.deserialize((data as Record)[idAttr]); - } else { - const newId = await this.newId(); - theId = idConfig.deserialize(newId.toString()); - } - const effectiveData = { - ...data, - } as Record; - effectiveData[idAttr] = theId; - - const clause = `INSERT INTO ${this.resource.state.routeName}`; - const keys = Object.keys(effectiveData).join(','); - const values = Object.values(effectiveData).map((d) => JSON.stringify(d).replace(/"/g, "'")).join(','); - const sql = `${clause} (${keys}) VALUES (${values});`; - const con = await this.db.connect(); - const stmt = await con.prepare(sql); - await stmt.run(); - const newData = { - ...effectiveData - }; - return newData as Data; - } - - async getTotalCount(query) { - assert(typeof this.db !== 'undefined'); - assert(typeof this.resource !== 'undefined'); - - const idAttr = this.resource.state.shared.get('idAttr'); - assert(typeof idAttr === 'string'); - - const con = await this.db.connect(); - const stmt = await con.prepare(` - SELECT COUNT(*) as c FROM ${this.resource.state.routeName}; - `); - const [data] = await stmt.all(); - return data['c'] as unknown as number; - } - - async getById(id: ID) { - assert(typeof this.db !== 'undefined'); - assert(typeof this.resource !== 'undefined'); - - const idAttr = this.resource.state.shared.get('idAttr'); - assert(typeof idAttr === 'string'); - - const con = await this.db.connect(); - const stmt = await con.prepare(` - SELECT * FROM ${this.resource.state.routeName} WHERE ${idAttr} = ${JSON.stringify(id).replace(/"/g, "'")}; - `); - const [data = null] = await stmt.all(); - return data as Data | null; - } - - async getSingle(query) { - assert(typeof this.db !== 'undefined'); - assert(typeof this.resource !== 'undefined'); - - const con = await this.db.connect(); - const stmt = await con.prepare(` - SELECT * FROM ${this.resource.state.routeName} LIMIT 1; - `); - const [data = null] = await stmt.all(); - return data as Data | null; - } - - async delete(id: ID) { - assert(typeof this.db !== 'undefined'); - assert(typeof this.resource !== 'undefined'); - - const idAttr = this.resource.state.shared.get('idAttr'); - assert(typeof idAttr === 'string'); - - const con = await this.db.connect(); - const stmt = await con.prepare(` - DELETE FROM ${this.resource.state.routeName} WHERE ${idAttr} = ${JSON.stringify(id).replace(/"/g, "'")}; - `); - await stmt.run(); - } - - async emplace(id: ID, data: Data) { - assert(typeof this.db !== 'undefined'); - assert(typeof this.resource !== 'undefined'); - - const idAttr = this.resource.state.shared.get('idAttr'); - assert(typeof idAttr === 'string'); - - const clause = `INSERT OR REPLACE INTO ${this.resource.state.routeName}`; - const keys = Object.keys(data).join(','); - const values = Object.values(data).map((d) => JSON.stringify(d).replace(/"/g, "'")).join(','); - const sql = `${clause} (${idAttr},${keys}) VALUES (${id},${values});`; - const con = await this.db.connect(); - const stmt = await con.prepare(sql); - const [newData] = await stmt.all(); - // TODO check if created flag - return [newData, false] as [Data, boolean]; - } - - async patch(id: ID, data: Partial) { - assert(typeof this.db !== 'undefined'); - assert(typeof this.resource !== 'undefined'); - - const idAttr = this.resource.state.shared.get('idAttr'); - assert(typeof idAttr === 'string'); - - const clause = `UPDATE ${this.resource.state.routeName}`; - const setParams = Object.entries(data).map(([key, value]) => ( - `${key} = ${JSON.stringify(value).replace(/"/g, "'")}` - )).join(','); - const sql = `${clause} SET ${setParams} WHERE ${idAttr} = ${JSON.stringify(id).replace(/"/g, "'")}` - const con = await this.db.connect(); - const stmt = await con.prepare(sql); - const [newData] = await stmt.all(); - return newData as Data; - } -} diff --git a/packages/data-sources/duckdb/tsconfig.json b/packages/data-sources/duckdb/tsconfig.json deleted file mode 100644 index 74083d7..0000000 --- a/packages/data-sources/duckdb/tsconfig.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "exclude": ["node_modules"], - "include": ["src", "types"], - "compilerOptions": { - "module": "ESNext", - "lib": ["ESNext"], - "importHelpers": true, - "declaration": true, - "sourceMap": true, - "rootDir": "./src", - "strict": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true, - "moduleResolution": "bundler", - "jsx": "react", - "esModuleInterop": true, - "target": "es2018", - "useDefineForClassFields": false, - "declarationMap": true - } -} diff --git a/packages/data-sources/file-jsonl/.gitignore b/packages/data-sources/file-jsonl/.gitignore deleted file mode 100644 index 53992de..0000000 --- a/packages/data-sources/file-jsonl/.gitignore +++ /dev/null @@ -1,107 +0,0 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -lerna-debug.log* - -# Diagnostic reports (https://nodejs.org/api/report.html) -report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json - -# Runtime data -pids -*.pid -*.seed -*.pid.lock - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage -*.lcov - -# nyc test coverage -.nyc_output - -# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# Bower dependency directory (https://bower.io/) -bower_components - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (https://nodejs.org/api/addons.html) -build/Release - -# Dependency directories -node_modules/ -jspm_packages/ - -# TypeScript v1 declaration files -typings/ - -# TypeScript cache -*.tsbuildinfo - -# Optional npm cache directory -.npm - -# Optional eslint cache -.eslintcache - -# Microbundle cache -.rpt2_cache/ -.rts2_cache_cjs/ -.rts2_cache_es/ -.rts2_cache_umd/ - -# Optional REPL history -.node_repl_history - -# Output of 'npm pack' -*.tgz - -# Yarn Integrity file -.yarn-integrity - -# dotenv environment variables file -.env -.env.production -.env.development - -# parcel-bundler cache (https://parceljs.org/) -.cache - -# Next.js build output -.next - -# Nuxt.js build / generate output -.nuxt -dist - -# Gatsby files -.cache/ -# Comment in the public line in if your project uses Gatsby and *not* Next.js -# https://nextjs.org/blog/next-9-1#public-directory-support -# public - -# vuepress build output -.vuepress/dist - -# Serverless directories -.serverless/ - -# FuseBox cache -.fusebox/ - -# DynamoDB Local files -.dynamodb/ - -# TernJS port file -.tern-port - -.npmrc diff --git a/packages/data-sources/file-jsonl/LICENSE b/packages/data-sources/file-jsonl/LICENSE deleted file mode 100644 index 5a4fdfd..0000000 --- a/packages/data-sources/file-jsonl/LICENSE +++ /dev/null @@ -1,7 +0,0 @@ -MIT License Copyright (c) 2024 TheoryOfNekomata - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/packages/data-sources/file-jsonl/package.json b/packages/data-sources/file-jsonl/package.json deleted file mode 100644 index 9097bcf..0000000 --- a/packages/data-sources/file-jsonl/package.json +++ /dev/null @@ -1,66 +0,0 @@ -{ - "name": "@modal-sh/yasumi-data-source-file-jsonl", - "version": "0.0.0", - "files": [ - "dist", - "src" - ], - "engines": { - "node": ">=16" - }, - "license": "MIT", - "keywords": [ - "pridepack" - ], - "devDependencies": { - "@types/node": "^20.11.0", - "pridepack": "2.6.0", - "tslib": "^2.6.2", - "typescript": "^5.3.3", - "vitest": "^1.2.0" - }, - "scripts": { - "prepublishOnly": "pridepack clean && pridepack build", - "build": "pridepack build", - "type-check": "pridepack check", - "clean": "pridepack clean", - "watch": "pridepack watch", - "start": "pridepack start", - "dev": "pridepack dev", - "test": "vitest" - }, - "dependencies": { - "@modal-sh/yasumi": "workspace:*" - }, - "private": false, - "description": "JSON lines file data source for yasumi.", - "repository": { - "url": "", - "type": "git" - }, - "homepage": "", - "bugs": { - "url": "" - }, - "author": "TheoryOfNekomata ", - "publishConfig": { - "access": "public" - }, - "types": "./dist/types/index.d.ts", - "main": "./dist/cjs/production/index.js", - "module": "./dist/esm/production/index.js", - "exports": { - ".": { - "development": { - "require": "./dist/cjs/development/index.js", - "import": "./dist/esm/development/index.js" - }, - "require": "./dist/cjs/production/index.js", - "import": "./dist/esm/production/index.js", - "types": "./dist/types/index.d.ts" - } - }, - "typesVersions": { - "*": {} - } -} diff --git a/packages/data-sources/file-jsonl/pridepack.json b/packages/data-sources/file-jsonl/pridepack.json deleted file mode 100644 index 0bc7a8f..0000000 --- a/packages/data-sources/file-jsonl/pridepack.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "target": "es2018" -} diff --git a/packages/data-sources/file-jsonl/src/index.ts b/packages/data-sources/file-jsonl/src/index.ts deleted file mode 100644 index 66973ad..0000000 --- a/packages/data-sources/file-jsonl/src/index.ts +++ /dev/null @@ -1,255 +0,0 @@ -import { readFile, writeFile } from 'fs/promises'; -import { join } from 'path'; -import { Resource, validation as v, BaseResourceType } from '@modal-sh/yasumi'; -import { DataSource, ResourceIdConfig } from '@modal-sh/yasumi/backend'; -import assert from 'assert'; - -export class ResourceNotPreparedError extends Error {} - -export class ResourceIdNotDesignatedError extends Error {} - -export class JsonLinesDataSource< - Schema extends v.BaseSchema = v.BaseSchema, - CurrentName extends string = string, - CurrentRouteName extends string = string, - Data extends object = v.Output, -> implements DataSource { - private path?: string; - - private resource?: Resource; - - data: Data[] = []; - - constructor(private readonly baseDir = '') { - // noop - } - - prepareResource(resource: Resource) { - this.path = join(this.baseDir, `${resource.state.routeName}.jsonl`); - resource.dataSource = resource.dataSource ?? this; - const originalResourceId = resource.id; - resource.id = (newIdAttr: NewIdAttr, params: ResourceIdConfig) => { - originalResourceId(newIdAttr, params); - return resource as Resource; - }; - this.resource = resource as any; - } - - async initialize() { - assert(typeof this.path === 'string', new ResourceNotPreparedError()); - - try { - const fileContents = await readFile(this.path, 'utf-8'); - const lines = fileContents.split('\n'); - this.data = lines.filter((l) => l.trim().length > 0).map((l) => JSON.parse(l)); - } catch (err) { - await writeFile(this.path, ''); - } - } - - async getTotalCount() { - return this.data.length; - } - - async getMultiple() { - return [...this.data]; - } - - async newId() { - const idConfig = this.resource?.state.shared.get('idConfig') as ResourceIdConfig; - assert(typeof idConfig !== 'undefined', new ResourceNotPreparedError()); - const theNewId = await idConfig.generationStrategy(this); - return theNewId as string; - } - - async getById(idSerialized: string) { - assert(typeof this.resource !== 'undefined', new ResourceNotPreparedError()); - assert(typeof this.path === 'string', new ResourceNotPreparedError()); - - const idAttr = this.resource.state.shared.get('idAttr'); - assert(typeof idAttr === 'string', new ResourceIdNotDesignatedError()); - - const idConfig = this.resource.state.shared.get('idConfig') as ResourceIdConfig | undefined; - assert(typeof idConfig !== 'undefined', new ResourceIdNotDesignatedError()); - - const id = idConfig.deserialize(idSerialized); - const foundData = this.data.find((s) => (s as any)[idAttr] === id); - - if (foundData) { - return { - ...foundData - }; - } - - return null; - } - - async create(data: Data) { - assert(typeof this.resource !== 'undefined', new ResourceNotPreparedError()); - assert(typeof this.path === 'string', new ResourceNotPreparedError()); - - const idAttr = this.resource.state.shared.get('idAttr'); - assert(typeof idAttr === 'string', new ResourceIdNotDesignatedError()); - - const idConfig = this.resource.state.shared.get('idConfig') as ResourceIdConfig | undefined; - assert(typeof idConfig !== 'undefined', new ResourceIdNotDesignatedError()); - - let theId: any; - const { [idAttr]: dataId, ...etcData } = data as Record; - if (typeof dataId !== 'undefined') { - theId = idConfig.deserialize((data as Record)[idAttr]); - } else { - const newId = await this.newId(); - theId = idConfig.deserialize(newId); - } - const newData = { - [idAttr]: theId, - ...etcData - } as Record; - - const now = Date.now(); // TODO how to serialize dates - const createdAt = this.resource.state.shared.get('createdAtAttr'); - if (typeof createdAt === 'string') { - newData[createdAt] = now; - } - - const updatedAt = this.resource.state.shared.get('updatedAtAttr'); - if (typeof updatedAt === 'string') { - newData[updatedAt] = now; - } - - const newCollection = [ - ...this.data, - newData - ]; - - await writeFile(this.path, newCollection.map((d) => JSON.stringify(d)).join('\n')); - - return newData as Data; - } - - async delete(idSerialized: string) { - assert(typeof this.resource !== 'undefined', new ResourceNotPreparedError()); - assert(typeof this.path === 'string', new ResourceNotPreparedError()); - - const idAttr = this.resource.state.shared.get('idAttr'); - assert(typeof idAttr === 'string', new ResourceIdNotDesignatedError()); - - const idConfig = this.resource.state.shared.get('idConfig') as ResourceIdConfig | undefined; - assert(typeof idConfig !== 'undefined', new ResourceIdNotDesignatedError()); - - const oldDataLength = this.data.length; - - const id = idConfig.deserialize(idSerialized); - const newData = this.data.filter((s) => !((s as any)[idAttr] === id)); - - await writeFile(this.path, newData.map((d) => JSON.stringify(d)).join('\n')); - - return oldDataLength !== newData.length; - } - - async emplace(idSerialized: string, dataWithId: Data) { - assert(typeof this.resource !== 'undefined', new ResourceNotPreparedError()); - assert(typeof this.path === 'string', new ResourceNotPreparedError()); - - const idAttr = this.resource.state.shared.get('idAttr'); - assert(typeof idAttr === 'string', new ResourceIdNotDesignatedError()); - - const idConfig = this.resource.state.shared.get('idConfig') as ResourceIdConfig | undefined; - assert(typeof idConfig !== 'undefined', new ResourceIdNotDesignatedError()); - - const existing = await this.getById(idSerialized); - const id = idConfig.deserialize(idSerialized); - const { [idAttr]: idFromResource, ...data } = (dataWithId as any); - const dataToEmplace = { - [idAttr]: id, - ...data, - } as Record; - - if (existing) { - const createdAt = this.resource.state.shared.get('createdAtAttr'); - if (typeof createdAt === 'string') { - dataToEmplace[createdAt] = (existing as Record)[createdAt]; - } - - const now = Date.now(); // TODO how to serialize dates - const updatedAt = this.resource.state.shared.get('updatedAtAttr'); - if (typeof updatedAt === 'string') { - dataToEmplace[updatedAt] = now; - } - - const newData = this.data.map((d) => { - if ((d as any)[idAttr] === id) { - return dataToEmplace; - } - - return d; - }); - - await writeFile(this.path, newData.map((d) => JSON.stringify(d)).join('\n')); - - return [dataToEmplace, false] as [Data, boolean]; - } - - const newData = await this.create(dataToEmplace as Data); - return [newData, true] as [Data, boolean]; - } - - async patch(idSerialized: string, data: Partial) { - assert(typeof this.resource !== 'undefined', new ResourceNotPreparedError()); - assert(typeof this.path === 'string', new ResourceNotPreparedError()); - - const idAttr = this.resource.state.shared.get('idAttr'); - assert(typeof idAttr === 'string', new ResourceIdNotDesignatedError()); - - const idConfig = this.resource.state.shared.get('idConfig') as ResourceIdConfig | undefined; - assert(typeof idConfig !== 'undefined', new ResourceIdNotDesignatedError()); - - const existing = await this.getById(idSerialized); - if (!existing) { - return null; - } - - const newItem = { - ...existing, - ...data, - } as Record; - - const createdAt = this.resource.state.shared.get('createdAtAttr'); - if (typeof createdAt === 'string') { - newItem[createdAt] = (existing as Record)[createdAt]; - } - - const now = Date.now(); // TODO how to serialize dates - const updatedAt = this.resource.state.shared.get('updatedAtAttr'); - if (typeof updatedAt === 'string') { - newItem[updatedAt] = now; - } - - const id = idConfig.deserialize(idSerialized); - const newData = this.data.map((d) => { - if ((d as any)[idAttr] === id) { - return newItem; - } - - return d; - }); - - await writeFile(this.path, newData.map((d) => JSON.stringify(d)).join('\n')); - return newItem as Data; - } -} diff --git a/packages/data-sources/file-jsonl/test/index.test.ts b/packages/data-sources/file-jsonl/test/index.test.ts deleted file mode 100644 index 89a0ba6..0000000 --- a/packages/data-sources/file-jsonl/test/index.test.ts +++ /dev/null @@ -1,236 +0,0 @@ -import {describe, it, expect, vi, Mock, beforeAll, beforeEach} from 'vitest'; -import { readFile, writeFile } from 'fs/promises'; -import { JsonLinesDataSource } from '../src'; -import { resource, validation as v, BaseResourceType } from '@modal-sh/yasumi'; -import {DataSource} from '@modal-sh/yasumi/dist/types/backend'; - -vi.mock('fs/promises'); - -const toJsonl = (dummyItems: unknown[]) => dummyItems.map((i) => JSON.stringify(i)).join('\n'); - -const ID_ATTR = 'id' as const; - -describe('prepareResource', () => { - beforeAll(() => { - const mockWriteFile = writeFile as Mock; - mockWriteFile.mockImplementation(() => { /* noop */ }) - }); - - it('works', () => { - const schema = v.object({}); - const r = resource(schema); - const ds = new JsonLinesDataSource(); - expect(() => ds.prepareResource(r)).not.toThrow(); - }); -}); - -describe('methods', () => { - const dummyItems = [ - { - id: 1, - name: 'foo', - }, - { - id: 2, - name: 'bar', - }, - { - id: 3, - name: 'baz', - }, - ]; - const schema = v.object({ - name: v.string(), - }); - let ds: DataSource>; - let mockGenerationStrategy: Mock; - beforeEach(() => { - mockGenerationStrategy = vi.fn(); - const r = resource(schema) - .id(ID_ATTR, { - generationStrategy: mockGenerationStrategy, - schema: v.any(), - serialize: (id) => id.toString(), - deserialize: (id) => Number(id?.toString() ?? 0), - }); - ds = new JsonLinesDataSource(); - ds.prepareResource(r); - }); - - beforeEach(() => { - const mockReadFile = readFile as Mock; - mockReadFile.mockReturnValueOnce(toJsonl(dummyItems)); - }); - - let mockWriteFile: Mock; - beforeEach(() => { - mockWriteFile = writeFile as Mock; - mockWriteFile.mockImplementationOnce(() => { /* noop */ }); - }); - - describe('initialize', () => { - it('works', async () => { - try { - await ds.initialize(); - } catch { - expect.fail('Could not initialize data source.'); - } - }); - }); - - describe('operations', () => { - beforeEach(async () => { - await ds.initialize(); - }); - - describe('getTotalCount', () => { - it('works', async () => { - if (typeof ds.getTotalCount !== 'function') { - return; - } - const totalCount = await ds.getTotalCount(); - expect(totalCount).toBe(dummyItems.length); - }); - }); - - describe('getMultiple', () => { - it('works', async () => { - const items = await ds.getMultiple(); - expect(items).toEqual(dummyItems); - }); - }); - - describe('getById', () => { - it('works', async () => { - const id = 2; - const item = await ds.getById(id.toString()); // ID is always a string because it originates from URLs - const expected = dummyItems.find((i) => i[ID_ATTR] === id); - expect(item).toEqual(expected); - }); - }); - - describe('getSingle', () => { - it('works', async () => { - if (typeof ds.getSingle !== 'function') { - // skip if data source doesn't offer this - return; - } - const item = await ds.getSingle(); - const expected = dummyItems[0]; - expect(item).toEqual(expected); - }); - }); - - describe('create', () => { - it('works', async () => { - const data = { - // notice we don't have IDs here, as it is expected to be generated by newId() - name: 'foo' - }; - const newItem = await ds.create(data); - - expect(mockWriteFile).toBeCalledWith( - expect.any(String), - toJsonl([ - ...dummyItems, - { - id: 0, - ...data, - } - ]) - ); - expect(newItem).toEqual({ id: 0, ...data}); - }); - }); - - describe('delete', () => { - it('works', async () => { - await ds.delete('1'); - expect(mockWriteFile).toBeCalledWith( - expect.any(String), - toJsonl(dummyItems.filter((d) => d[ID_ATTR] !== 1)), - ); - }); - }); - - describe('emplace', () => { - it('replaces existing data', async () => { - const data = { - [ID_ATTR]: 2, - name: 'foo', - }; - const { id, ...etcData } = data; - const newItem = await ds.emplace( - id.toString(), - etcData, - ); - - expect(mockWriteFile).toBeCalledWith( - expect.any(String), - toJsonl(dummyItems.map((d) => - d[ID_ATTR] === data[ID_ATTR] - // ID will be defined first, since we are just writing to file, we need strict ordering - ? { [ID_ATTR]: id, ...etcData } - : d - )), - ); - expect(newItem).toEqual([data, false]); - }); - - it('creates new data', async () => { - const data = { - [ID_ATTR]: 4, - name: 'quux', - }; - const { [ID_ATTR]: id, ...etcData } = data; - const newItem = await ds.emplace( - id.toString(), - etcData, - ); - - expect(mockWriteFile).toBeCalledWith( - expect.any(String), - toJsonl([ - ...dummyItems, - data - ]), - ); - expect(newItem).toEqual([data, true]); - }); - }); - - describe('patch', () => { - it('works', async () => { - const data = { - [ID_ATTR]: 2, - name: 'foo', - }; - const { id, ...etcData } = data; - const newItem = await ds.emplace( - id.toString(), - etcData, - ); - - expect(mockWriteFile).toBeCalledWith( - expect.any(String), - toJsonl(dummyItems.map((d) => - d[ID_ATTR] === data[ID_ATTR] - // ID will be defined first, since we are just writing to file, we need strict ordering - ? { [ID_ATTR]: id, ...etcData } - : d - )), - ); - expect(newItem).toBeDefined(); - }); - }); - - describe('newId', () => { - it('works', async () => { - const v = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER); - mockGenerationStrategy.mockResolvedValueOnce(v); - const id = await ds.newId(); - expect(id).toBe(v); - }); - }); - }); -}); diff --git a/packages/data-sources/file-jsonl/tsconfig.json b/packages/data-sources/file-jsonl/tsconfig.json deleted file mode 100644 index 74083d7..0000000 --- a/packages/data-sources/file-jsonl/tsconfig.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "exclude": ["node_modules"], - "include": ["src", "types"], - "compilerOptions": { - "module": "ESNext", - "lib": ["ESNext"], - "importHelpers": true, - "declaration": true, - "sourceMap": true, - "rootDir": "./src", - "strict": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true, - "moduleResolution": "bundler", - "jsx": "react", - "esModuleInterop": true, - "target": "es2018", - "useDefineForClassFields": false, - "declarationMap": true - } -} diff --git a/packages/examples/cms-web-api/.gitignore b/packages/examples/cms-web-api/.gitignore deleted file mode 100644 index 8383a05..0000000 --- a/packages/examples/cms-web-api/.gitignore +++ /dev/null @@ -1,108 +0,0 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -lerna-debug.log* - -# Diagnostic reports (https://nodejs.org/api/report.html) -report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json - -# Runtime data -pids -*.pid -*.seed -*.pid.lock - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage -*.lcov - -# nyc test coverage -.nyc_output - -# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# Bower dependency directory (https://bower.io/) -bower_components - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (https://nodejs.org/api/addons.html) -build/Release - -# Dependency directories -node_modules/ -jspm_packages/ - -# TypeScript v1 declaration files -typings/ - -# TypeScript cache -*.tsbuildinfo - -# Optional npm cache directory -.npm - -# Optional eslint cache -.eslintcache - -# Microbundle cache -.rpt2_cache/ -.rts2_cache_cjs/ -.rts2_cache_es/ -.rts2_cache_umd/ - -# Optional REPL history -.node_repl_history - -# Output of 'npm pack' -*.tgz - -# Yarn Integrity file -.yarn-integrity - -# dotenv environment variables file -.env -.env.production -.env.development - -# parcel-bundler cache (https://parceljs.org/) -.cache - -# Next.js build output -.next - -# Nuxt.js build / generate output -.nuxt -dist - -# Gatsby files -.cache/ -# Comment in the public line in if your project uses Gatsby and *not* Next.js -# https://nextjs.org/blog/next-9-1#public-directory-support -# public - -# vuepress build output -.vuepress/dist - -# Serverless directories -.serverless/ - -# FuseBox cache -.fusebox/ - -# DynamoDB Local files -.dynamodb/ - -# TernJS port file -.tern-port - -.npmrc -*.jsonl diff --git a/packages/examples/cms-web-api/bruno/Check Allowed Post Operations.bru b/packages/examples/cms-web-api/bruno/Check Allowed Post Operations.bru deleted file mode 100644 index 17d52b0..0000000 --- a/packages/examples/cms-web-api/bruno/Check Allowed Post Operations.bru +++ /dev/null @@ -1,11 +0,0 @@ -meta { - name: Check Allowed Post Operations - type: http - seq: 10 -} - -options { - url: http://localhost:6969/api/posts/9ba60691-0cd3-4e8a-9f44-e92b19fcacbc - body: none - auth: none -} diff --git a/packages/examples/cms-web-api/bruno/Check Allowed Posts Operations.bru b/packages/examples/cms-web-api/bruno/Check Allowed Posts Operations.bru deleted file mode 100644 index 72f933d..0000000 --- a/packages/examples/cms-web-api/bruno/Check Allowed Posts Operations.bru +++ /dev/null @@ -1,11 +0,0 @@ -meta { - name: Check Allowed Posts Operations - type: http - seq: 11 -} - -options { - url: http://localhost:6969/api/posts - body: none - auth: none -} diff --git a/packages/examples/cms-web-api/bruno/Create Post with ID.bru b/packages/examples/cms-web-api/bruno/Create Post with ID.bru deleted file mode 100644 index 9dbe97e..0000000 --- a/packages/examples/cms-web-api/bruno/Create Post with ID.bru +++ /dev/null @@ -1,19 +0,0 @@ -meta { - name: Create Post with ID - type: http - seq: 8 -} - -put { - url: http://localhost:6969/api/posts/5fac64d6-d261-42bb-a67b-bc7e1955a7e2 - body: json - auth: none -} - -body:json { - { - "title": "Emplaced Post", - "content": "Created post at ID", - "id": "5fac64d6-d261-42bb-a67b-bc7e1955a7e2" - } -} diff --git a/packages/examples/cms-web-api/bruno/Create Post.bru b/packages/examples/cms-web-api/bruno/Create Post.bru deleted file mode 100644 index f647859..0000000 --- a/packages/examples/cms-web-api/bruno/Create Post.bru +++ /dev/null @@ -1,18 +0,0 @@ -meta { - name: Create Post - type: http - seq: 2 -} - -post { - url: http://localhost:6969/api/posts - body: json - auth: none -} - -body:json { - { - "title": "New Post", - "content": "Hello there" - } -} diff --git a/packages/examples/cms-web-api/bruno/Delete Post.bru b/packages/examples/cms-web-api/bruno/Delete Post.bru deleted file mode 100644 index 5433b82..0000000 --- a/packages/examples/cms-web-api/bruno/Delete Post.bru +++ /dev/null @@ -1,15 +0,0 @@ -meta { - name: Delete Post - type: http - seq: 9 -} - -delete { - url: http://localhost:6969/api/posts/5fac64d6-d261-42bb-a67b-bc7e1955a7e2 - body: none - auth: none -} - -headers { - ~Accept-Language: tl -} diff --git a/packages/examples/cms-web-api/bruno/Get Root.bru b/packages/examples/cms-web-api/bruno/Get Root.bru deleted file mode 100644 index 0bfe252..0000000 --- a/packages/examples/cms-web-api/bruno/Get Root.bru +++ /dev/null @@ -1,11 +0,0 @@ -meta { - name: Get Root - type: http - seq: 1 -} - -get { - url: http://localhost:6969/api - body: none - auth: none -} diff --git a/packages/examples/cms-web-api/bruno/Get Single Post.bru b/packages/examples/cms-web-api/bruno/Get Single Post.bru deleted file mode 100644 index 246f00b..0000000 --- a/packages/examples/cms-web-api/bruno/Get Single Post.bru +++ /dev/null @@ -1,11 +0,0 @@ -meta { - name: Get Single Post - type: http - seq: 4 -} - -get { - url: http://localhost:6969/api/posts/9ba60691-0cd3-4e8a-9f44-e92b19fcacbc - body: none - auth: none -} diff --git a/packages/examples/cms-web-api/bruno/Modify Post (Delta).bru b/packages/examples/cms-web-api/bruno/Modify Post (Delta).bru deleted file mode 100644 index 329a874..0000000 --- a/packages/examples/cms-web-api/bruno/Modify Post (Delta).bru +++ /dev/null @@ -1,25 +0,0 @@ -meta { - name: Modify Post (Delta) - type: http - seq: 6 -} - -patch { - url: http://localhost:6969/api/posts/9ba60691-0cd3-4e8a-9f44-e92b19fcacbc - body: json - auth: none -} - -headers { - Content-Type: application/json-patch+json -} - -body:json { - [ - { - "op": "replace", - "path": "content", - "value": "I changed the value via delta." - } - ] -} diff --git a/packages/examples/cms-web-api/bruno/Modify Post (Merge).bru b/packages/examples/cms-web-api/bruno/Modify Post (Merge).bru deleted file mode 100644 index e431c77..0000000 --- a/packages/examples/cms-web-api/bruno/Modify Post (Merge).bru +++ /dev/null @@ -1,23 +0,0 @@ -meta { - name: Modify Post (Merge) - type: http - seq: 5 -} - -patch { - url: http://localhost:6969/api/posts/9ba60691-0cd3-4e8a-9f44-e92b19fcacbc - body: json - auth: none -} - -headers { - Content-Type: application/merge-patch+json -} - -body:json { - { - "title": "Modified Post", - "content": "I changed the content via merge." - } - -} diff --git a/packages/examples/cms-web-api/bruno/Query Posts.bru b/packages/examples/cms-web-api/bruno/Query Posts.bru deleted file mode 100644 index da2b4aa..0000000 --- a/packages/examples/cms-web-api/bruno/Query Posts.bru +++ /dev/null @@ -1,11 +0,0 @@ -meta { - name: Query Posts - type: http - seq: 3 -} - -get { - url: http://localhost:6969/api/posts - body: none - auth: none -} diff --git a/packages/examples/cms-web-api/bruno/Replace Post.bru b/packages/examples/cms-web-api/bruno/Replace Post.bru deleted file mode 100644 index d4340b6..0000000 --- a/packages/examples/cms-web-api/bruno/Replace Post.bru +++ /dev/null @@ -1,19 +0,0 @@ -meta { - name: Replace Post - type: http - seq: 7 -} - -put { - url: http://localhost:6969/api/posts/9ba60691-0cd3-4e8a-9f44-e92b19fcacbc - body: json - auth: none -} - -body:json { - { - "title": "Replaced Post", - "content": "The old content is gone.", - "id": "9ba60691-0cd3-4e8a-9f44-e92b19fcacbc" - } -} diff --git a/packages/examples/cms-web-api/bruno/bruno.json b/packages/examples/cms-web-api/bruno/bruno.json deleted file mode 100644 index e833fff..0000000 --- a/packages/examples/cms-web-api/bruno/bruno.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "version": "1", - "name": "cms-web-api", - "type": "collection", - "ignore": [ - "node_modules", - ".git" - ], - "presets": { - "requestType": "http", - "requestUrl": "http://localhost:6969/api/" - } -} \ No newline at end of file diff --git a/packages/examples/cms-web-api/package.json b/packages/examples/cms-web-api/package.json deleted file mode 100644 index afd5b93..0000000 --- a/packages/examples/cms-web-api/package.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "name": "@modal-sh/yasumi-example-cms-web-api", - "version": "0.0.0", - "files": [ - "dist", - "src" - ], - "engines": { - "node": ">=16" - }, - "keywords": [ - "pridepack" - ], - "devDependencies": { - "@types/node": "^20.11.0", - "pridepack": "2.6.0", - "tslib": "^2.6.2", - "typescript": "^5.3.3", - "vitest": "^1.2.0" - }, - "dependencies": { - "@modal-sh/yasumi": "workspace:*", - "@modal-sh/yasumi-server-http": "workspace:*", - "@modal-sh/yasumi-data-source-file-jsonl": "workspace:*", - "tsx": "^4.7.1" - }, - "scripts": { - "prepublishOnly": "pridepack clean && pridepack build", - "build": "pridepack build", - "type-check": "pridepack check", - "clean": "pridepack clean", - "watch": "pridepack watch", - "start": "tsx src/index.ts", - "dev": "tsx watch src/index.ts", - "test": "vitest" - }, - "private": true, - "description": "CMS example service for Yasumi", - "repository": { - "url": "", - "type": "git" - }, - "homepage": "", - "bugs": { - "url": "" - }, - "author": "TheoryOfNekomata ", - "publishConfig": { - "access": "restricted" - } -} diff --git a/packages/examples/cms-web-api/pridepack.json b/packages/examples/cms-web-api/pridepack.json deleted file mode 100644 index 0bc7a8f..0000000 --- a/packages/examples/cms-web-api/pridepack.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "target": "es2018" -} diff --git a/packages/examples/cms-web-api/src/index.ts b/packages/examples/cms-web-api/src/index.ts deleted file mode 100644 index 79b7020..0000000 --- a/packages/examples/cms-web-api/src/index.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { application, resource, validation as v } from '@modal-sh/yasumi'; -import * as http from '@modal-sh/yasumi-server-http'; -import { randomUUID } from 'crypto'; -import { JsonLinesDataSource } from '@modal-sh/yasumi-data-source-file-jsonl'; -import { constants } from 'http2'; -import TAGALOG from './languages/tl'; - -const UuidIdConfig = { - // TODO bind to data source - generationStrategy: () => Promise.resolve(randomUUID()), - schema: v.string(), - serialize: (v: unknown) => v?.toString() ?? '', - deserialize: (v: string) => v, // TODO resolve bytes/non-formatted UUIDs -}; - -const User = resource( - v.object({ - email: v.string([v.email()]), - password: v.string(), - createdAt: v.date(), - updatedAt: v.date(), - }) -) - .name('User') - .route('users') - .id('id', UuidIdConfig) - .canFetchItem() - .canFetchCollection() - .canCreate() - .canEmplace() - .canPatch() - .canDelete(); - -const Post = resource( - v.object({ - title: v.string(), - content: v.string(), - }) -) - .name('Post') - .route('posts') - .id('id', UuidIdConfig) - .createdAt('createdAt') - .updatedAt('updatedAt') - .canFetchItem() - .canFetchCollection() - .canCreate() - .canEmplace() - .canPatch() - .canDelete(); - -const app = application({ - name: 'cms' -}) - .language(TAGALOG) - .resource(User) - .resource(Post); - -const backend = app.createBackend({ - dataSource: new JsonLinesDataSource(), -}) - .use(http.httpExtender) - .throwsErrorOnDeletingNotFound(); - -const server = backend.createServer('http', { - basePath: '/api', -}) - .defaultErrorHandler((_req, res) => () => { - throw new http.ErrorPlainResponse('urlNotFound', { - statusCode: constants.HTTP_STATUS_NOT_FOUND, - res, - }); - // throw new http.ErrorPlainResponse('notImplemented', { - // statusCode: constants.HTTP_STATUS_NOT_IMPLEMENTED, - // res, - // }); - }); - -server.listen(6969); diff --git a/packages/examples/cms-web-api/src/languages/tl.ts b/packages/examples/cms-web-api/src/languages/tl.ts deleted file mode 100644 index b096fb5..0000000 --- a/packages/examples/cms-web-api/src/languages/tl.ts +++ /dev/null @@ -1,282 +0,0 @@ -import { Language } from '@modal-sh/yasumi'; - -export default { - name: 'tl' as const, - statusMessages: { - unableToSerializeResponse: 'Hindi Maitala ang Tugon', - unableToEncodeResponse: 'Hindi Ma-Encode ang Tugon', - unableToBindResourceDataSource: 'Hindi Ma-bind ang $RESOURCE na Pinagmulan ng Datos', - unableToInitializeResourceDataSource: 'Hindi Ma-initialize ang $RESOURCE na Pinagmulan ng Datos', - unableToFetchResourceCollection: 'Hindi Ma-fetch ang $RESOURCE Collection', - unableToFetchResource: 'Hindi Ma-fetch ang $RESOURCE', - unableToDeleteResource: 'Hindi Ma-delete ang $RESOURCE', - languageNotAcceptable: 'Hindi Tinatanggap ang Wika', - characterSetNotAcceptable: 'Hindi Tinatanggap ang Set ng mga Titik', - unableToDeserializeResource: 'Hindi Ma-deserialize ang $RESOURCE', - unableToDecodeResource: 'Hindi Ma-decode ang $RESOURCE', - mediaTypeNotAcceptable: 'Hindi Tinatanggap ang Uri ng Media', - methodNotAllowed: 'Bawal ang Paraang Ginamit', - urlNotFound: 'Hindi Nahanap ang URL', - badRequest: 'Maling Paghiling', - ok: 'OK', - provideOptions: 'Magbigay ng mga Pagpipilian', - resourceCollectionQueried: 'Hinanapan ang $RESOURCE Collection', - resourceCollectionFetched: 'Nakuha ang $RESOURCE Collection', - resourceFetched: 'Nakuha ang $RESOURCE', - resourceNotFound: 'Hindi Nahanap ang $RESOURCE', - deleteNonExistingResource: 'I-delete ang Nawawalang $RESOURCE', - resourceDeleted: 'Na-delete ang $RESOURCE', - unableToDeserializeRequest: 'Hindi Ma-deserialize ang Hiling', - patchNonExistingResource: 'I-patch ang Nawawalang $RESOURCE', - unableToPatchResource: 'Hindi Ma-patch ang $RESOURCE', - invalidResourcePatch: 'Maling Patak ng $RESOURCE', - invalidResourcePatchType: 'Maling Uri ng Patak ng $RESOURCE', - invalidResource: 'Maling $RESOURCE', - resourcePatched: 'Na-patch ang $RESOURCE', - resourceCreated: 'Na-gawa ang $RESOURCE', - resourceReplaced: 'Naipalit ang $RESOURCE', - unableToGenerateIdFromResourceDataSource: 'Hindi Makagawa ng ID Mula sa Pinagmulang Data Source ng $RESOURCE', - unableToAssignIdFromResourceDataSource: 'Hindi Makapaglagay ng ID Mula sa Pinagmulang Data Source ng $RESOURCE', - unableToEmplaceResource: 'Hindi Ma-emplace ang $RESOURCE', - resourceIdNotGiven: 'Hindi Ibinigay ang ID ng $RESOURCE', - unableToCreateResource: 'Hindi Makagawa ng $RESOURCE', - notImplemented: 'Hindi Pa Na-implement', - internalServerError: 'Internal Server Error', - }, - bodies: { - badRequest: [ - 'Isang maling hiling ang naipadala.', - [ - 'Kumpirmahin kung mayroon ang hiling na katawan ng lahat ng kinakailangang mga attribute para sa endpoint na ito.', - 'Kumpirmahin kung mayroon lang ang hiling na katawan ng mga tamang attribute para sa endpoint na ito.', - 'Kumpirmahin kung ang katawan ng hiling ay tugma sa schema para sa pinagmulang pinagmulan ng endpoint na ito.', - 'Kumpirmahin kung ang hiling ay naaangkop para sa endpoint na ito.', - ], - ], - languageNotAcceptable: [ - 'Hindi kayang i-proseso ng server ang isang tugon na angkop para sa kinakailangang wika ng kliyente.', - [ - 'Pumili mula sa mga available na wika sa serbisyong ito.', - 'Makipag-ugnayan sa administrator upang magbigay ng lokal na pagsasalin para sa mga kinakailangang pangangailangan ng kliyente.', - ], - ], - characterSetNotAcceptable: [ - 'Hindi kayang i-proseso ng server ang isang tugon na angkop para sa kinakailangang set ng mga titik ng kliyente.', - [ - 'Pumili mula sa mga available na set ng mga titik sa serbisyong ito.', - 'Makipag-ugnayan sa administrator upang magbigay ng lokal na pagsasalin para sa mga kinakailangang pangangailangan ng kliyente.', - ], - ], - mediaTypeNotAcceptable: [ - 'Hindi kayang i-proseso ng server ang isang tugon na angkop para sa kinakailangang uri ng media ng kliyente.', - [ - 'Pumili mula sa mga available na uri ng media sa serbisyong ito.', - 'Makipag-ugnayan sa administrator upang magbigay ng lokal na pagsasalin para sa mga kinakailangang pangangailangan ng kliyente.', - ], - ], - deleteNonExistingResource: [ - 'Sinubukan ng kliyente na i-delete ang isang pinagmulang hindi umiiral na resource.', - [ - 'Siguruhing umiiral pa rin ang pinagmulan.', - 'Siguruhing ang tamang paraan ng pagsasaad ay ibinigay.', - ], - ], - internalServerError: [ - 'May hindi kilalang error na nangyari sa loob ng serbisyo.', - [ - 'Subukang muli ang hiling mamaya.', - 'Makipag-ugnayan sa administrator kung nananatili ang serbisyo sa isang degradadong o hindi gumagana na kalagayan.', - ], - ], - invalidResource: [ - 'Ang hiling ay may maling estruktura o kulang sa ilang mga attribute.', - [ - 'Kumpirmahin kung ang katawan ng hiling ay may lahat ng kinakailangang mga attribute para sa endpoint na ito.', - 'Kumpirmahin kung ang katawan ng hiling ay may lamang mga tamang attribute para sa endpoint na ito.', - 'Kumpirmahin kung ang katawan ng hiling ay tugma sa schema para sa pinagmulang pinagmulan ng endpoint na ito.', - ], - ], - invalidResourcePatch: [ - 'Ang hiling ay may maling patch data.', - [ - 'Kumpirmahin kung ang tamang uri ng patch ay tinukoy sa datos ng hiling.', - 'Kumpirmahin kung ang katawan ng hiling ay may lahat ng kinakailangang mga attribute para sa endpoint na ito.', - 'Kumpirmahin kung ang katawan ng hiling ay may lamang mga tamang attribute para sa endpoint na ito.', - 'Kumpirmahin kung ang katawan ng hiling ay tugma sa schema para sa pinagmulang pinagmulan ng endpoint na ito.', - ], - ], - invalidResourcePatchType: [ - 'Ang hiling ay may mali o hindi suportadong uri ng patch data.', - [ - 'Kumpirmahin kung ang tamang uri ng patch ay tinukoy sa datos ng hiling.', - 'Kumpirmahin kung ang katawan ng hiling ay may lahat ng kinakailangang mga attribute para sa endpoint na ito.', - 'Kumpirmahin kung ang katawan ng hiling ay may lamang mga tamang attribute para sa endpoint na ito.', - 'Kumpirmahin kung ang katawan ng hiling ay tugma sa schema para sa pinagmulang pinagmulan ng endpoint na ito.', - ], - ], - methodNotAllowed: [ - 'Isang hiling na may maling o hindi suportadong paraan ang ginawa.', - [ - 'Kumpirmahin kung ang paraang ginamit sa hiling ay naaangkop para sa endpoint na ito.', - 'Kumpirmahin kung ang kliyente ay awtorisado na magawa ang paraang ito sa endpoint na ito.', - ] - ], - notImplemented: [ - 'Wala pang implementasyon ang serbisyo para sa inaksyunang endpoint.', - [ - 'Subukang muli ang hiling mamaya.', - 'Makipag-ugnayan sa administrator kung nananatili ang serbisyo sa isang degradadong o hindi gumagana na kalagayan.', - ], - ], - patchNonExistingResource: [ - 'Subukan ng kliyente na i-patch ang isang pinagmulang hindi umiiral na resource.', - [ - 'Siguruhing umiiral pa rin ang pinagmulan.', - 'Siguruhing ang tamang paraan ng pagsasaad ay ibinigay.', - ], - ], - resourceIdNotGiven: [ - 'Hindi ibinigay ang ID ng pinagmulang resource para sa inaksyunang endpoint.', - [ - 'Kumpirmahin kung ibinigay at wasto ang ID ng pinagmulang sa URL.', - 'Kumpirmahin kung ang paraang hiling ay naaangkop para sa endpoint na ito.', - ], - ], - unableToAssignIdFromResourceDataSource: [ - 'Hindi ma-assign ang ID mula sa kaugnay na pinagmulang pinagmulan ng datos ng resource.', - [ - 'Subukang muli ang hiling mamaya.', - 'Makipag-ugnayan sa administrator tungkol sa mga nawawalang kongfigurasyon o kawalan ng kagamitan.', - ], - ], - unableToBindResourceDataSource: [ - 'Hindi ma-bind ang pinagmulan ng datos mula sa resource.', - [ - 'Subukang muli ang hiling mamaya.', - 'Makipag-ugnayan sa administrator tungkol sa mga nawawalang kongfigurasyon o kawalan ng kagamitan.', - ], - ], - unableToCreateResource: [ - 'Nagkaroon ng error sa paggawa ng resource.', - [ - 'Kumpirmahin kung ang paraang hiling ay naaangkop para sa endpoint na ito.', - 'Kumpirmahin kung ang katawan ng hiling ay may lahat ng kinakailangang mga attribute para sa endpoint na ito.', - 'Kumpirmahin kung ang katawan ng hiling ay may lamang mga tamang attribute para sa endpoint na ito.', - 'Kumpirmahin kung ang katawan ng hiling ay tugma sa schema para sa pinagmulang pinagmulan ng endpoint na ito.', - 'Subukang muli ang hiling mamaya.', - 'Makipag-ugnayan sa administrator tungkol sa mga nawawalang kongfigurasyon o kawalan ng kagamitan.', - ], - ], - unableToDecodeResource: [ - 'Hindi ma-decode ang byte array ng resource para sa ibinigay na set ng mga titik.', - [ - 'Pumili mula sa mga available na set ng mga titik sa serbisyong ito.', - 'Makipag-ugnayan sa administrator upang magbigay ng lokal na pagsasalin para sa mga kinakailangang pangangailangan ng kliyente.', - ], - ], - unableToDeleteResource: [ - 'Nagkaroon ng error sa pag-delete ng resource.', - [ - 'Kumpirmahin kung ang paraang hiling ay naaangkop para sa endpoint na ito.', - 'Subukang muli ang hiling mamaya.', - 'Makipag-ugnayan sa administrator tungkol sa mga nawawalang kongfigurasyon o kawalan ng kagamitan.', - ], - ], - unableToDeserializeRequest: [ - 'Hindi ma-deserialize ang decoded na byte array ng hiling para sa ibinigay na uri ng media.', - [ - 'Pumili mula sa mga available na uri ng media sa serbisyong ito.', - 'Makipag-ugnayan sa administrator upang magbigay ng lokal na pagsasalin para sa mga kinakailangang pangangailangan ng kliyente.', - ], - ], - unableToDeserializeResource: [ - 'Hindi ma-deserialize ang decoded na resource para sa ibinigay na uri ng media.', - [ - 'Pumili mula sa mga available na uri ng media sa serbisyong ito.', - 'Makipag-ugnayan sa administrator upang magbigay ng lokal na pagsasalin para sa mga kinakailangang pangangailangan ng kliyente.', - ], - ], - unableToEmplaceResource: [ - 'Nagkaroon ng error sa pag-e-emplace ng resource.', - [ - 'Kumpirmahin kung ang paraang hiling ay naaangkop para sa endpoint na ito.', - 'Kumpirmahin kung ang katawan ng hiling ay may lahat ng kinakailangang mga attribute para sa endpoint na ito.', - 'Kumpirmahin kung ang katawan ng hiling ay may lamang mga tamang attribute para sa endpoint na ito.', - 'Kumpirmahin kung ang katawan ng hiling ay tugma sa schema para sa pinagmulang pinagmulan ng endpoint na ito.', - 'Subukang muli ang hiling mamaya.', - 'Makipag-ugnayan sa administrator tungkol sa mga nawawalang kongfigurasyon o kawalan ng kagamitan.', - ], - ], - unableToEncodeResponse: [ - 'Hindi ma-encode ang datos ng tugon para sa ibinigay na set ng mga titik.', - [ - 'Pumili mula sa mga available na set ng mga titik sa serbisyong ito.', - 'Makipag-ugnayan sa administrator upang magbigay ng lokal na pagsasalin para sa mga kinakailangang pangangailangan ng kliyente.', - ], - ], - unableToFetchResource: [ - 'Nagkaroon ng error sa pag-fetch ng resource.', - [ - 'Kumpirmahin kung ang paraang hiling ay naaangkop para sa endpoint na ito.', - 'Subukang muli ang hiling mamaya.', - 'Makipag-ugnayan sa administrator tungkol sa mga nawawalang kongfigurasyon o kawalan ng kagamitan.', - ], - ], - unableToFetchResourceCollection: [ - 'Nagkaroon ng error sa pag-fetch ng koleksyon ng resource.', - [ - 'Kumpirmahin kung ang paraang hiling ay naaangkop para sa endpoint na ito.', - 'Subukang muli ang hiling mamaya.', - 'Makipag-ugnayan sa administrator tungkol sa mga nawawalang kongfigurasyon o kawalan ng kagamitan.', - ], - ], - unableToGenerateIdFromResourceDataSource: [ - 'Ang kaugnay na pinagmulang pinagmulan ng datos para sa resource ay hindi makapag-produce ng ID.', - [ - 'Subukang muli ang hiling mamaya.', - 'Makipag-ugnayan sa administrator tungkol sa mga nawawalang kongfigurasyon o kawalan ng kagamitan.', - ], - ], - unableToInitializeResourceDataSource: [ - 'Ang kaugnay na pinagmulang pinagmulan ng datos para sa resource ay hindi ma-connect para sa paggamit.', - [ - 'Subukang muli ang hiling mamaya.', - 'Makipag-ugnayan sa administrator tungkol sa mga nawawalang kongfigurasyon o kawalan ng kagamitan.', - ], - ], - unableToPatchResource: [ - 'Nagkaroon ng error sa pag-patch ng resource.', - [ - 'Kumpirmahin kung ang paraang hiling ay naaangkop para sa endpoint na ito.', - 'Kumpirmahin kung ang katawan ng hiling ay may lahat ng kinakailangang mga attribute para sa endpoint na ito.', - 'Kumpirmahin kung ang katawan ng hiling ay may lamang mga tamang attribute para sa endpoint na ito.', - 'Kumpirmahin kung ang katawan ng hiling ay tugma sa schema para sa pinagmulang pinagmulan ng endpoint na ito.', - 'Subukang muli ang hiling mamaya.', - 'Makipag-ugnayan sa administrator tungkol sa mga nawawalang kongfigurasyon o kawalan ng kagamitan.', - ], - ], - unableToSerializeResponse: [ - 'Hindi ma-serialize ang datos ng tugon para sa ibinigay na uri ng media.', - [ - 'Pumili mula sa mga available na uri ng media sa serbisyong ito.', - 'Makipag-ugnayan sa administrator upang magbigay ng lokal na pagsasalin para sa mga kinakailangang pangangailangan ng kliyente.', - ], - ], - urlNotFound: [ - 'Hindi nahanap ang isang endpoint sa ibinigay na URL.', - [ - 'Kumpirmahin kung ang URL ng hiling ay tama.', - 'Subukang muli ang hiling mamaya.', - 'Makipag-ugnayan sa administrator tungkol sa mga nawawalang kongfigurasyon o kawalan ng kagamitan.', - ], - ], - resourceNotFound: [ - 'Hindi nahanap ang resource sa ibinigay na URL.', - [ - 'Kumpirmahin kung ang URL ng hiling ay tama.', - 'Subukang muli ang hiling mamaya.', - 'Makipag-ugnayan sa administrator tungkol sa mga nawawalang kongfigurasyon o kawalan ng kagamitan.', - ], - ], - }, -} satisfies Language; diff --git a/packages/examples/cms-web-api/tsconfig.json b/packages/examples/cms-web-api/tsconfig.json deleted file mode 100644 index 74083d7..0000000 --- a/packages/examples/cms-web-api/tsconfig.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "exclude": ["node_modules"], - "include": ["src", "types"], - "compilerOptions": { - "module": "ESNext", - "lib": ["ESNext"], - "importHelpers": true, - "declaration": true, - "sourceMap": true, - "rootDir": "./src", - "strict": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true, - "moduleResolution": "bundler", - "jsx": "react", - "esModuleInterop": true, - "target": "es2018", - "useDefineForClassFields": false, - "declarationMap": true - } -} diff --git a/packages/examples/duckdb/.gitignore b/packages/examples/duckdb/.gitignore deleted file mode 100644 index b9f3b75..0000000 --- a/packages/examples/duckdb/.gitignore +++ /dev/null @@ -1,109 +0,0 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -lerna-debug.log* - -# Diagnostic reports (https://nodejs.org/api/report.html) -report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json - -# Runtime data -pids -*.pid -*.seed -*.pid.lock - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage -*.lcov - -# nyc test coverage -.nyc_output - -# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# Bower dependency directory (https://bower.io/) -bower_components - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (https://nodejs.org/api/addons.html) -build/Release - -# Dependency directories -node_modules/ -jspm_packages/ - -# TypeScript v1 declaration files -typings/ - -# TypeScript cache -*.tsbuildinfo - -# Optional npm cache directory -.npm - -# Optional eslint cache -.eslintcache - -# Microbundle cache -.rpt2_cache/ -.rts2_cache_cjs/ -.rts2_cache_es/ -.rts2_cache_umd/ - -# Optional REPL history -.node_repl_history - -# Output of 'npm pack' -*.tgz - -# Yarn Integrity file -.yarn-integrity - -# dotenv environment variables file -.env -.env.production -.env.development - -# parcel-bundler cache (https://parceljs.org/) -.cache - -# Next.js build output -.next - -# Nuxt.js build / generate output -.nuxt -dist - -# Gatsby files -.cache/ -# Comment in the public line in if your project uses Gatsby and *not* Next.js -# https://nextjs.org/blog/next-9-1#public-directory-support -# public - -# vuepress build output -.vuepress/dist - -# Serverless directories -.serverless/ - -# FuseBox cache -.fusebox/ - -# DynamoDB Local files -.dynamodb/ - -# TernJS port file -.tern-port - -.npmrc -*.db -*.db.wal diff --git a/packages/examples/duckdb/package.json b/packages/examples/duckdb/package.json deleted file mode 100644 index 1329b4e..0000000 --- a/packages/examples/duckdb/package.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "name": "@modal-sh/yasumi-example-duckdb", - "version": "0.0.0", - "files": [ - "dist", - "src" - ], - "engines": { - "node": ">=16" - }, - "keywords": [ - "pridepack" - ], - "devDependencies": { - "@types/node": "^20.11.0", - "pridepack": "2.6.0", - "tslib": "^2.6.2", - "typescript": "^5.3.3", - "vitest": "^1.2.0" - }, - "dependencies": { - "@modal-sh/yasumi": "workspace:*", - "@modal-sh/yasumi-server-http": "workspace:*", - "@modal-sh/yasumi-data-source-duckdb": "workspace:*", - "tsx": "^4.7.1" - }, - "scripts": { - "prepublishOnly": "pridepack clean && pridepack build", - "build": "pridepack build", - "type-check": "pridepack check", - "clean": "pridepack clean", - "watch": "pridepack watch", - "start": "tsx src/index.ts", - "dev": "tsx watch src/index.ts", - "test": "vitest" - }, - "private": true, - "description": "DuckDB-powered example service.", - "repository": { - "url": "", - "type": "git" - }, - "homepage": "", - "bugs": { - "url": "" - }, - "author": "TheoryOfNekomata ", - "publishConfig": { - "access": "restricted" - } -} diff --git a/packages/examples/duckdb/pridepack.json b/packages/examples/duckdb/pridepack.json deleted file mode 100644 index 0bc7a8f..0000000 --- a/packages/examples/duckdb/pridepack.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "target": "es2018" -} diff --git a/packages/examples/duckdb/src/index.ts b/packages/examples/duckdb/src/index.ts deleted file mode 100644 index 76e8600..0000000 --- a/packages/examples/duckdb/src/index.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { resource, application, validation as v } from '@modal-sh/yasumi'; -import * as http from '@modal-sh/yasumi-server-http'; -import { DuckDbDataSource, AutoincrementIdConfig } from '@modal-sh/yasumi-data-source-duckdb'; -import { constants } from 'http2'; - -const Post = resource( - v.object({ - title: v.string(), - content: v.string(), - }) -) - .name('Post') - .route('posts') - .id('id', AutoincrementIdConfig) - .createdAt('createdAt') - .updatedAt('updatedAt') - .canFetchItem() - .canFetchCollection() - .canCreate() - .canEmplace() - .canPatch() - .canDelete(); - -const app = application({ - name: 'duckdb-service' -}) - .resource(Post); - -const backend = app.createBackend({ - dataSource: new DuckDbDataSource('test.db'), - }) - .use(http.httpExtender) - .showTotalItemCountOnGetCollection() - .throwsErrorOnDeletingNotFound(); - -const server = backend.createServer('http', { - basePath: '/api', - }) - .defaultErrorHandler((_req, res) => () => { - throw new http.ErrorPlainResponse('urlNotFound', { - statusCode: constants.HTTP_STATUS_NOT_FOUND, - res, - }); - // throw new http.ErrorPlainResponse('notImplemented', { - // statusCode: constants.HTTP_STATUS_NOT_IMPLEMENTED, - // res, - // }); - }); - -server.listen(6969); diff --git a/packages/examples/duckdb/tsconfig.json b/packages/examples/duckdb/tsconfig.json deleted file mode 100644 index 74083d7..0000000 --- a/packages/examples/duckdb/tsconfig.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "exclude": ["node_modules"], - "include": ["src", "types"], - "compilerOptions": { - "module": "ESNext", - "lib": ["ESNext"], - "importHelpers": true, - "declaration": true, - "sourceMap": true, - "rootDir": "./src", - "strict": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true, - "moduleResolution": "bundler", - "jsx": "react", - "esModuleInterop": true, - "target": "es2018", - "useDefineForClassFields": false, - "declarationMap": true - } -} diff --git a/packages/servers/http/.gitignore b/packages/servers/http/.gitignore deleted file mode 100644 index 53992de..0000000 --- a/packages/servers/http/.gitignore +++ /dev/null @@ -1,107 +0,0 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -lerna-debug.log* - -# Diagnostic reports (https://nodejs.org/api/report.html) -report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json - -# Runtime data -pids -*.pid -*.seed -*.pid.lock - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage -*.lcov - -# nyc test coverage -.nyc_output - -# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# Bower dependency directory (https://bower.io/) -bower_components - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (https://nodejs.org/api/addons.html) -build/Release - -# Dependency directories -node_modules/ -jspm_packages/ - -# TypeScript v1 declaration files -typings/ - -# TypeScript cache -*.tsbuildinfo - -# Optional npm cache directory -.npm - -# Optional eslint cache -.eslintcache - -# Microbundle cache -.rpt2_cache/ -.rts2_cache_cjs/ -.rts2_cache_es/ -.rts2_cache_umd/ - -# Optional REPL history -.node_repl_history - -# Output of 'npm pack' -*.tgz - -# Yarn Integrity file -.yarn-integrity - -# dotenv environment variables file -.env -.env.production -.env.development - -# parcel-bundler cache (https://parceljs.org/) -.cache - -# Next.js build output -.next - -# Nuxt.js build / generate output -.nuxt -dist - -# Gatsby files -.cache/ -# Comment in the public line in if your project uses Gatsby and *not* Next.js -# https://nextjs.org/blog/next-9-1#public-directory-support -# public - -# vuepress build output -.vuepress/dist - -# Serverless directories -.serverless/ - -# FuseBox cache -.fusebox/ - -# DynamoDB Local files -.dynamodb/ - -# TernJS port file -.tern-port - -.npmrc diff --git a/packages/servers/http/LICENSE b/packages/servers/http/LICENSE deleted file mode 100644 index 5a4fdfd..0000000 --- a/packages/servers/http/LICENSE +++ /dev/null @@ -1,7 +0,0 @@ -MIT License Copyright (c) 2024 TheoryOfNekomata - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/packages/servers/http/package.json b/packages/servers/http/package.json deleted file mode 100644 index 64665f9..0000000 --- a/packages/servers/http/package.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "name": "@modal-sh/yasumi-server-http", - "version": "0.0.0", - "files": [ - "dist", - "src" - ], - "engines": { - "node": ">=16" - }, - "license": "MIT", - "keywords": [ - "pridepack" - ], - "devDependencies": { - "@types/negotiator": "^0.6.3", - "@types/node": "^20.11.0", - "pridepack": "2.6.0", - "tslib": "^2.6.2", - "typescript": "^5.3.3", - "vitest": "^1.2.0" - }, - "dependencies": { - "@modal-sh/yasumi": "workspace:*", - "negotiator": "^0.6.3" - }, - "scripts": { - "prepublishOnly": "pridepack clean && pridepack build", - "build": "pridepack build", - "type-check": "pridepack check", - "clean": "pridepack clean", - "watch": "pridepack watch", - "start": "pridepack start", - "dev": "pridepack dev", - "test": "vitest" - }, - "private": false, - "description": "HTTP server for Yasumi backend.", - "repository": { - "url": "", - "type": "git" - }, - "homepage": "", - "bugs": { - "url": "" - }, - "author": "TheoryOfNekomata ", - "publishConfig": { - "access": "public" - }, - "types": "./dist/types/index.d.ts", - "main": "./dist/cjs/production/index.js", - "module": "./dist/esm/production/index.js", - "exports": { - ".": { - "development": { - "require": "./dist/cjs/development/index.js", - "import": "./dist/esm/development/index.js" - }, - "require": "./dist/cjs/production/index.js", - "import": "./dist/esm/production/index.js", - "types": "./dist/types/index.d.ts" - } - }, - "typesVersions": { - "*": {} - } -} diff --git a/packages/servers/http/pridepack.json b/packages/servers/http/pridepack.json deleted file mode 100644 index 0bc7a8f..0000000 --- a/packages/servers/http/pridepack.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "target": "es2018" -} diff --git a/packages/servers/http/src/core.ts b/packages/servers/http/src/core.ts deleted file mode 100644 index 507e746..0000000 --- a/packages/servers/http/src/core.ts +++ /dev/null @@ -1,829 +0,0 @@ -import http, { createServer as httpCreateServer } from 'http'; -import { createServer as httpCreateSecureServer } from 'https'; -import {constants,} from 'http2'; -import EventEmitter from 'events'; -import { - AllowedMiddlewareSpecification, - Backend, - BackendState, - Middleware, - RequestContext, - RequestDecorator, - Response, - Server, - DataSource, -} from '@modal-sh/yasumi/backend'; -import { - BaseResourceType, - CanPatchSpec, - DELTA_SCHEMA, - getAcceptPatchString, - getAcceptPostString, - LanguageDefaultErrorStatusMessageKey, - PATCH_CONTENT_MAP_TYPE, PATCH_CONTENT_TYPES, - PatchContentType, queryMediaTypes, - Resource, - validation as v, - isTextMediaType, -} from '@modal-sh/yasumi'; -import { - handleGetRoot, handleOptions, -} from './handlers/default'; -import { - handleCreateItem, - handleDeleteItem, - handleEmplaceItem, - handleGetCollection, - handleGetItem, - handlePatchItem, - handleQueryCollection, -} from './handlers/resource'; -import {getBody} from './utils'; -import {decorateRequestWithBackend} from './decorators/backend'; -import {decorateRequestWithMethod} from './decorators/method'; -import {decorateRequestWithUrl} from './decorators/url'; -import {ErrorPlainResponse, PlainResponse} from './response'; - -type RequiredResource = Required>['resource']; - -interface ResourceWithDataSource extends Omit { - dataSource: DataSource; -} - -interface ResourceRequestContext extends Omit { - resource: ResourceWithDataSource; -} - -export interface HttpServer extends Server { - readonly listening: boolean; - on(event: string, cb: (...args: unknown[]) => unknown): this; - close(callback?: (err?: Error) => void): this; - listen(...args: Parameters): this; - defaultErrorHandler(errorHandler: ErrorHandler): this; -} - -declare module '@modal-sh/yasumi/backend' { - interface RequestContext extends http.IncomingMessage { - body?: unknown; - } - - interface Middleware { - (req: Req, res: Res): undefined | Response | Promise; - } - - interface Backend { - createServer(type: 'http', options?: CreateServerParams): T; - } -} - -const constructPostSchema = (resource: Resource) => { - return resource.schema; -}; - -const constructPutSchema = (resource: Resource, mainResourceId?: string) => { - if (typeof mainResourceId === 'undefined') { - return resource.schema; - } - - const schema = resource.schema.type === 'object' ? resource.schema as unknown as v.ObjectSchema : resource.schema; - const idAttr = resource.state.shared.get('idAttr') as string; - const idConfig = resource.state.shared.get('idConfig') as any; - return ( - schema.type === 'object' - ? v.merge([ - schema as v.ObjectSchema, - v.object({ - [idAttr]: v.transform( - v.any(), - input => idConfig!.serialize(input), - v.literal(mainResourceId) - ) - }) - ]) - : schema - ); -}; - -const constructPatchSchema = (resource: Resource) => { - const schema = resource.schema.type === 'object' ? resource.schema as unknown as v.ObjectSchema : resource.schema; - - if (resource.schema.type !== 'object') { - return resource.schema; - } - - const schemaChoices = { - merge: v.partial( - schema as v.ObjectSchema, - (schema as v.ObjectSchema).rest, - (schema as v.ObjectSchema).pipe - ), - delta: v.array(DELTA_SCHEMA), - } - - const selectedSchemaChoices = Object.entries(schemaChoices) - .filter(([key]) => resource.state.canPatch[key as CanPatchSpec]) - .map(([, value]) => value); - - return v.union(selectedSchemaChoices); -}; -// TODO add a way to define custom middlewares -const defaultCollectionMiddlewares: AllowedMiddlewareSpecification[] = [ - { - method: 'QUERY', - middleware: handleQueryCollection, - allowed: (resource) => resource.state.canFetchCollection, - }, - { - method: 'GET', - middleware: handleGetCollection, - allowed: (resource) => resource.state.canFetchCollection, - }, - { - method: 'POST', - middleware: handleCreateItem, - allowed: (resource) => resource.state.canCreate, - constructBodySchema: constructPostSchema, - }, -]; - -const defaultItemMiddlewares: AllowedMiddlewareSpecification[] = [ - { - method: 'GET', - middleware: handleGetItem, - allowed: (resource) => resource.state.canFetchItem, - }, - { - method: 'PUT', - middleware: handleEmplaceItem, - constructBodySchema: constructPutSchema, - allowed: (resource) => resource.state.canEmplace, - }, - { - method: 'PATCH', - middleware: handlePatchItem, - constructBodySchema: constructPatchSchema, - allowed: (resource) => resource.state.canPatch.merge || resource.state.canPatch.delta, - }, - { - method: 'DELETE', - middleware: handleDeleteItem, - allowed: (resource) => resource.state.canDelete, - }, -]; - -export interface CreateServerParams { - basePath?: string; - host?: string; - cert?: string; - key?: string; - requestTimeout?: number; - // CQRS - streamResponses?: boolean; -} - -class CqrsEventEmitter extends EventEmitter { - -} - -export type ErrorHandler = (req: RequestContext, res: http.ServerResponse) => (err?: E) => never; - -interface ServerState { - requestDecorators: Set; - defaultErrorHandler?: ErrorHandler; -} - -export const httpExtender = (backendState: BackendState, backend: Backend) => { - const originalCreateServer = backend.createServer; - backend.createServer = (type: 'http', serverParamsRaw = {}) => { - const theServerRaw = originalCreateServer(type, serverParamsRaw); - if (type !== 'http') { - return theServerRaw; - } - - const serverParams = serverParamsRaw as CreateServerParams; - const state: ServerState = { - requestDecorators: new Set(), - defaultErrorHandler: undefined, - }; - - const theServer = { - ...theServerRaw, - get listening() { return server.listening }, - listen(...args: Parameters) { - server.listen(...args); - return this; - }, - close(callback?: (err?: Error) => void) { - server.close(callback); - return this; - }, - on(...args: Parameters) { - server.on(args[0], args[1]); - return this; - }, - requestDecorator(requestDecorator: RequestDecorator) { - state.requestDecorators.add(requestDecorator); - return this; - }, - defaultErrorHandler(errorHandler: ErrorHandler) { - state.defaultErrorHandler = errorHandler; - return this; - } - } as HttpServer; - - const isHttps = 'key' in serverParams && 'cert' in serverParams; - const theRes = new CqrsEventEmitter(); - - http.METHODS.push('QUERY'); - const server = isHttps - ? httpCreateSecureServer({ - key: serverParams.key, - cert: serverParams.cert, - requestTimeout: serverParams.requestTimeout, - // TODO add custom methods - }) - : httpCreateServer({ - requestTimeout: serverParams.requestTimeout, - }); - - const handleMiddlewares = async (currentHandlerState: Awaited>, currentMiddleware: AllowedMiddlewareSpecification, req: ResourceRequestContext) => { - const { method: middlewareMethod, middleware, constructBodySchema} = currentMiddleware; - const effectiveMethod = req.method === 'HEAD' ? 'GET' : req.method; - - if (effectiveMethod !== middlewareMethod) { - return currentHandlerState; - } - - if (typeof currentHandlerState !== 'undefined') { - return currentHandlerState; - } - - if (effectiveMethod === 'QUERY') { - const contentTypeHeader = req.headers['content-type'] ?? 'application/octet-stream'; - const fragments = contentTypeHeader.replace(/\s+/g, ' ').split(';'); - const mediaType = fragments[0]; - const charsetParam = ( - fragments - .map((s) => s.trim()) - .find((f) => f.startsWith('charset=')) - - ?? ( - isTextMediaType(mediaType) - ? 'charset=utf-8' - : 'charset=binary' - ) - ); - const [_charsetKey, charsetRaw] = charsetParam.split('=').map((s) => s.trim()); - const charset = ( - ( - (charsetRaw.startsWith('"') && charsetRaw.endsWith('"')) - || (charsetRaw.startsWith("'") && charsetRaw.endsWith("'")) - ) - ? charsetRaw.slice(1, -1).trim() - : charsetRaw.trim() - ) ?? (isTextMediaType(mediaType) ? 'utf-8' : 'binary'); - - const theBodyBuffer = await getBody(req); - const encodingPair = req.backend.app.charsets.get(charset); - if (typeof encodingPair === 'undefined') { - throw new ErrorPlainResponse('unableToDecodeResource', { - statusCode: constants.HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE, - res: theRes, - }); - } - - const deserializerPair = Object.values(queryMediaTypes) - .find((a) => a.name === mediaType); - if (typeof deserializerPair === 'undefined') { - throw new ErrorPlainResponse( - 'unableToDeserializeRequest', - { - statusCode: constants.HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE, - res: theRes, - }, - ); - } - - const theBodyStr = encodingPair.decode(theBodyBuffer); - req.body = deserializerPair.deserialize(theBodyStr); - } else if (typeof constructBodySchema === 'function') { - const bodySchema = constructBodySchema(req.resource, req.resourceId); - const contentTypeHeader = req.headers['content-type'] ?? 'application/octet-stream'; - const fragments = contentTypeHeader.replace(/\s+/g, ' ').split(';'); - const mediaType = fragments[0]; - const charsetParam = ( - fragments - .map((s) => s.trim()) - .find((f) => f.startsWith('charset=')) - - ?? ( - isTextMediaType(mediaType) - ? 'charset=utf-8' - : 'charset=binary' - ) - ); - const [_charsetKey, charsetRaw] = charsetParam.split('=').map((s) => s.trim()); - const charset = ( - ( - (charsetRaw.startsWith('"') && charsetRaw.endsWith('"')) - || (charsetRaw.startsWith("'") && charsetRaw.endsWith("'")) - ) - ? charsetRaw.slice(1, -1).trim() - : charsetRaw.trim() - ) ?? (isTextMediaType(mediaType) ? 'utf-8' : 'binary'); - - if (effectiveMethod === 'POST' && PATCH_CONTENT_TYPES.includes(mediaType as PatchContentType)) { - throw new ErrorPlainResponse('invalidResource', { - statusCode: constants.HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE, - res: theRes, - headers: { - 'Accept-Post': getAcceptPostString(req.backend.app.mediaTypes), - }, - }); - } - - if (effectiveMethod === 'PATCH') { - const isPatchEnabled = req.resource.state.canPatch[PATCH_CONTENT_MAP_TYPE[mediaType as PatchContentType]]; - if (!isPatchEnabled) { - throw new ErrorPlainResponse('invalidResourcePatchType', { - statusCode: constants.HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE, - res: theRes, - headers: { - 'Accept-Patch': getAcceptPatchString(req.resource.state.canPatch), - }, - }); - } - } - - const theBodyBuffer = await getBody(req); - const encodingPair = req.backend.app.charsets.get(charset); - if (typeof encodingPair === 'undefined') { - throw new ErrorPlainResponse('unableToDecodeResource', { - statusCode: constants.HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE, - res: theRes, - }); - } - const deserializerPair = req.backend.app.mediaTypes.get(mediaType); - if (typeof deserializerPair === 'undefined') { - throw new ErrorPlainResponse('unableToDeserializeResource', { - statusCode: constants.HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE, - res: theRes, - }); - } - const theBodyStr = encodingPair.decode(theBodyBuffer); - const theBody = deserializerPair.deserialize(theBodyStr); - try { - // for validation, I wonder why an empty object is returned for PATCH when both methods are enabled - req.body = await v.parseAsync(bodySchema, theBody, {abortEarly: false, abortPipeEarly: false}); - req.body = theBody; - } catch (errRaw) { - const err = errRaw as v.ValiError; - // todo use error message key for each method - // TODO better error reporting, localizable messages - // TODO handle error handlers' errors - if (Array.isArray(err.issues)) { - if (req.method === 'PATCH' && req.headers['content-type']?.startsWith('application/json-patch+json')) { - throw new ErrorPlainResponse('invalidResourcePatch', { - statusCode: constants.HTTP_STATUS_BAD_REQUEST, - body: err.issues.map((i) => ( - `${i.path?.map((p) => p.key)?.join('.') ?? i.reason}: ${i.message}` - )), - res: theRes, - }); - } - - throw new ErrorPlainResponse('invalidResource', { - statusCode: constants.HTTP_STATUS_BAD_REQUEST, - body: err.issues.map((i) => ( - `${i.path?.map((p) => p.key)?.join('.') ?? i.reason}: ${i.message}` - )), - res: theRes, - }); - } - } - } - - const result = await middleware(req, theRes); - - // HEAD is just GET without the response body - if (req.method === 'HEAD' && result instanceof PlainResponse) { - const { body: _, ...etcResult } = result; - - return new PlainResponse({ - ...etcResult, - res: theRes, - }); - } - - return result; - }; - - const processRequest = (middlewares: AllowedMiddlewareSpecification[]) => async (req: ResourceRequestContext) => { - const { resource } = req; - if (typeof resource === 'undefined') { - throw new ErrorPlainResponse('resourceNotFound', { - statusCode: constants.HTTP_STATUS_NOT_FOUND, - res: theRes, - }); - } - - if (req.method === 'OPTIONS') { - return handleOptions(middlewares)(req, theRes); - } - - if (typeof resource.dataSource === 'undefined') { - throw new ErrorPlainResponse('unableToBindResourceDataSource', { - statusCode: constants.HTTP_STATUS_INTERNAL_SERVER_ERROR, - res: theRes, - }); - } - - try { - await resource.dataSource.initialize(); - } catch (cause) { - throw new ErrorPlainResponse( - 'unableToInitializeResourceDataSource', - { - cause, - statusCode: constants.HTTP_STATUS_INTERNAL_SERVER_ERROR, - res: theRes, - } - ); - } - - const middlewareResponse = await middlewares.reduce>( - async (currentHandlerStatePromise, currentMiddleware) => { - const currentHandlerState = await currentHandlerStatePromise; - return await handleMiddlewares(currentHandlerState, currentMiddleware, req); - }, - Promise.resolve>(undefined) - ) as Awaited>; - - if (typeof middlewareResponse === 'undefined') { - throw new ErrorPlainResponse('resourceNotFound', { - statusCode: constants.HTTP_STATUS_NOT_FOUND, - res: theRes, - }); - } - - return middlewareResponse as Awaited> - }; - - const defaultRequestDecorators = [ - decorateRequestWithMethod, - decorateRequestWithUrl(serverParams), - decorateRequestWithBackend(backendState), - ]; - - const decorateRequest = async (reqRaw: http.IncomingMessage) => { - const effectiveRequestDecorators = [ - ...defaultRequestDecorators, - ...Array.from(state.requestDecorators), - ]; - - return await effectiveRequestDecorators.reduce( - async (resultRequestPromise, decorator) => { - const resultRequest = await resultRequestPromise; - const decoratedRequest = await decorator(resultRequest); - // TODO log decorators - return decoratedRequest; - }, - Promise.resolve(reqRaw as RequestContext) - ); - }; - - const handleResourceError = (processRequestErrRaw: Error) => (resourceReq: ResourceRequestContext, res: http.ServerResponse) => { - const finalErr = processRequestErrRaw as ErrorPlainResponse; - const headers = finalErr.headers ?? {}; - const language = resourceReq.cn.language ?? resourceReq.backend.cn.language; - const mediaType = resourceReq.cn.mediaType ?? resourceReq.backend.cn.mediaType; - const charset = resourceReq.cn.charset ?? resourceReq.backend.cn.charset; - let encoded: Buffer | undefined; - let serialized; - - const body = finalErr.body ?? language.bodies[(finalErr.statusMessage ?? 'internalServerError') as LanguageDefaultErrorStatusMessageKey]; - try { - serialized = mediaType.serialize(body); - } catch (cause) { - handleError( - new ErrorPlainResponse('unableToSerializeResponse', { - statusCode: constants.HTTP_STATUS_INTERNAL_SERVER_ERROR, - res, - cause, - }) - )(resourceReq, res); - return; - } - - try { - encoded = typeof serialized !== 'undefined' ? charset.encode(serialized) : undefined; - } catch (cause) { - handleError( - new ErrorPlainResponse('unableToEncodeResponse', { - statusCode: constants.HTTP_STATUS_INTERNAL_SERVER_ERROR, - res, - cause, - }) - )(resourceReq, res); - return; - } - - headers['Content-Type'] = [ - mediaType.name, - typeof serialized !== 'undefined' ? `charset=${charset.name}` : '', - ] - .filter((s) => s.length > 0) - .join('; '); - - res.statusMessage = language.statusMessages[ - finalErr.statusMessage ?? 'internalServerError' - ]?.replace(/\$RESOURCE/g, - resourceReq.resource.state.itemName); - res.writeHead(finalErr.statusCode ?? constants.HTTP_STATUS_INTERNAL_SERVER_ERROR, headers); - if (typeof encoded !== 'undefined') { - res.end(encoded); - return; - } - res.end(); - }; - - const handleError = (err: Error) => (req: RequestContext, res: http.ServerResponse) => { - if ('resource' in req && typeof req.resource !== 'undefined') { - handleResourceError(err)(req as ResourceRequestContext, res); - return; - } - - const finalErr = err as ErrorPlainResponse; - const headers = finalErr.headers ?? {}; - const language = req.backend.cn.language; - const mediaType = req.backend.cn.mediaType; - const charset = req.backend.cn.charset; - - let encoded: Buffer | undefined; - let serialized; - const body = finalErr.body ?? language.bodies[(finalErr.statusMessage ?? 'internalServerError') as LanguageDefaultErrorStatusMessageKey]; - try { - serialized = mediaType.serialize(body); - } catch (cause) { - // TODO logging - res.statusMessage = language.statusMessages['unableToSerializeResponse']; - res.writeHead(constants.HTTP_STATUS_INTERNAL_SERVER_ERROR); - res.end(); - return; - } - - try { - encoded = typeof serialized !== 'undefined' ? charset.encode(serialized) : undefined; - } catch (cause) { - // TODO logging - res.statusMessage = language.statusMessages['unableToEncodeResponse']; - res.writeHead(constants.HTTP_STATUS_INTERNAL_SERVER_ERROR); - res.end(); - return; - } - - headers['Content-Type'] = [ - mediaType.name, - typeof serialized !== 'undefined' ? `charset=${charset.name}` : '', - ] - .filter((s) => s.length > 0) - .join('; '); - - res.statusMessage = typeof finalErr.statusMessage !== 'undefined' ? language.statusMessages[finalErr.statusMessage] : ''; - res.writeHead( - finalErr.statusCode ?? constants.HTTP_STATUS_INTERNAL_SERVER_ERROR, - headers, - ) - if (typeof encoded !== 'undefined') { - res.end(encoded); - return; - } - res.end(); - }; - - const handleResourceResponse = (resourceReq: ResourceRequestContext, res: http.ServerResponse) => (middlewareState: Response) => { - const language = resourceReq.cn.language ?? resourceReq.backend.cn.language; - const mediaType = resourceReq.cn.mediaType ?? resourceReq.backend.cn.mediaType; - const charset = resourceReq.cn.charset ?? resourceReq.backend.cn.charset; - - const headers: Record = { - ...( - middlewareState.headers ?? {} - ), - 'Content-Language': language.name, - }; - if (middlewareState instanceof http.ServerResponse) { - // TODO streaming responses - middlewareState.writeHead(constants.HTTP_STATUS_ACCEPTED, headers); - return; - } - if (middlewareState instanceof PlainResponse) { - let encoded: Buffer | undefined; - if (typeof middlewareState.body !== 'undefined') { - let serialized; - try { - serialized = mediaType.serialize(middlewareState.body); - } catch (cause) { - const headers: Record = { - 'Content-Language': language.name, - }; - if (resourceReq.method === 'POST') { - headers['Accept-Post'] = Array.from(resourceReq.backend.app.mediaTypes.keys()) - .filter((t) => !Object.keys(PATCH_CONTENT_MAP_TYPE).includes(t)) - .join(','); - } else if (resourceReq.method === 'PATCH') { - headers['Accept-Patch'] = Array.from(Object.entries(PATCH_CONTENT_MAP_TYPE)) - .filter(([, value]) => Object.keys(resourceReq.resource.state.canPatch).includes(value)) - .map(([contentType]) => contentType) - .join(','); - } - - handleError(new ErrorPlainResponse('unableToSerializeResponse', { - cause, - statusCode: constants.HTTP_STATUS_INTERNAL_SERVER_ERROR, - headers, - res, - }))(resourceReq, res); - return; - } - - try { - encoded = charset.encode(serialized); - } catch (cause) { - handleError(new ErrorPlainResponse('unableToEncodeResponse', { - cause, - statusCode: constants.HTTP_STATUS_INTERNAL_SERVER_ERROR, - headers, - res, - }))(resourceReq, res); - return; - } - - headers['Content-Type'] = [ - mediaType.name, - `charset=${charset.name}`, - ].join('; '); - } - - const statusMessageKey = middlewareState.statusMessage ? language.statusMessages[middlewareState.statusMessage] : undefined; - res.statusMessage = statusMessageKey?.replace(/\$RESOURCE/g, resourceReq.resource.state.itemName) ?? ''; - res.writeHead(middlewareState.statusCode, headers); - if (typeof encoded !== 'undefined') { - res.end(encoded); - return; - } - res.end(); - return; - } - - handleError(new ErrorPlainResponse('urlNotFound', { - statusCode: constants.HTTP_STATUS_NOT_FOUND, - res, - }))(resourceReq, res); - }; - - const handleResponse = (resourceReq: RequestContext, res: http.ServerResponse) => (middlewareState: Response) => { - if ('resource' in resourceReq && typeof resourceReq.resource !== 'undefined') { - handleResourceResponse(resourceReq as ResourceRequestContext, res)(middlewareState); - return; - } - - const language = resourceReq.cn.language ?? resourceReq.backend.cn.language; - const mediaType = resourceReq.cn.mediaType ?? resourceReq.backend.cn.mediaType; - const charset = resourceReq.cn.charset ?? resourceReq.backend.cn.charset; - - const headers: Record = { - ...( - middlewareState.headers ?? {} - ), - 'Content-Language': language.name, - }; - if (middlewareState instanceof http.ServerResponse) { - // TODO streaming responses - middlewareState.writeHead(constants.HTTP_STATUS_ACCEPTED, headers); - return; - } - if (middlewareState instanceof PlainResponse) { - let encoded: Buffer | undefined; - if (typeof middlewareState.body !== 'undefined') { - let serialized; - try { - serialized = mediaType.serialize(middlewareState.body); - } catch (cause) { - const headers: Record = { - 'Content-Language': language.name, - }; - if (resourceReq.method === 'POST') { - headers['Accept-Post'] = Array.from(resourceReq.backend.app.mediaTypes.keys()) - .filter((t) => !Object.keys(PATCH_CONTENT_MAP_TYPE).includes(t)) - .join(','); - } - - handleError(new ErrorPlainResponse('unableToSerializeResponse', { - cause, - statusCode: constants.HTTP_STATUS_INTERNAL_SERVER_ERROR, - headers, - res, - }))(resourceReq, res); - return; - } - - try { - encoded = charset.encode(serialized); - } catch (cause) { - handleError(new ErrorPlainResponse('unableToEncodeResponse', { - cause, - statusCode: constants.HTTP_STATUS_INTERNAL_SERVER_ERROR, - headers, - res, - }))(resourceReq, res); - return; - } - - headers['Content-Type'] = [ - mediaType.name, - `charset=${charset.name}`, - ].join('; '); - } - - const statusMessageKey = middlewareState.statusMessage ? language.statusMessages[middlewareState.statusMessage] : undefined; - res.statusMessage = statusMessageKey ?? ''; - res.writeHead(middlewareState.statusCode, headers); - if (typeof encoded !== 'undefined') { - res.end(encoded); - return; - } - res.end(); - return; - } - - handleError(new ErrorPlainResponse('urlNotFound', { - statusCode: constants.HTTP_STATUS_NOT_FOUND, - res, - }))(resourceReq, res); - }; - - const handleRequest = async (reqRaw: RequestContext, res: http.ServerResponse) => { - const plainReq = await decorateRequest(reqRaw); // TODO add type safety here - - if (plainReq.url === '/' || plainReq.url === '') { - const response = await handleGetRoot(plainReq as ResourceRequestContext, theRes); - if (typeof response === 'undefined') { - handleError( - new ErrorPlainResponse('internalServerError', { - statusCode: constants.HTTP_STATUS_INTERNAL_SERVER_ERROR, - res, - }) - )(reqRaw, res); - return; - } - handleResponse(plainReq as ResourceRequestContext, res)(response); - return; - } - - if (typeof plainReq.resource !== 'undefined') { - const resourceReq = plainReq as ResourceRequestContext; - // TODO custom middlewares - const effectiveMiddlewares = ( - typeof resourceReq.resourceId === 'string' - ? defaultItemMiddlewares - : defaultCollectionMiddlewares - ); - const middlewares = effectiveMiddlewares.filter((m) => m.allowed(resourceReq.resource)); - // TODO listen to res.on('response') - const processRequestFn = processRequest(middlewares); - let middlewareState: Response; - try { - middlewareState = await processRequestFn(resourceReq) as any; // TODO fix this - } catch (processRequestErrRaw) { - // TODO add error handlers - handleError(processRequestErrRaw as Error)(resourceReq, res); - return; - } - - handleResponse(resourceReq, res)(middlewareState); - return; - } - - try { - state.defaultErrorHandler?.(reqRaw, res)(); - } catch (err) { - handleError(err as Error)(reqRaw, res); - return; - } - - handleError( - new ErrorPlainResponse('internalServerError', { - statusCode: constants.HTTP_STATUS_INTERNAL_SERVER_ERROR, - res, - }) - )(reqRaw, res); - }; - - server.on('request', handleRequest); - - return theServer; - }; - - return backend; -}; diff --git a/packages/servers/http/src/decorators/backend/content-negotiation.ts b/packages/servers/http/src/decorators/backend/content-negotiation.ts deleted file mode 100644 index cc65c35..0000000 --- a/packages/servers/http/src/decorators/backend/content-negotiation.ts +++ /dev/null @@ -1,29 +0,0 @@ -import {ContentNegotiation} from '@modal-sh/yasumi'; -import {RequestDecorator} from '@modal-sh/yasumi/backend'; -import Negotiator from 'negotiator'; - -declare module '@modal-sh/yasumi/backend' { - interface RequestContext { - cn: Partial; - } -} - -export const decorateRequestWithContentNegotiation: RequestDecorator = (req) => { - const negotiator = new Negotiator(req); - - const availableLanguages = Array.from(req.backend.app.languages.values() ?? []); - const availableCharsets = Array.from(req.backend.app.charsets.values() ?? []); - const availableMediaTypes = Array.from(req.backend.app.mediaTypes.values() ?? []); - - const languageCandidate = negotiator.language(availableLanguages.map((l) => l.name)) ?? req.backend.cn.language.name; - const charsetCandidate = negotiator.charset(availableCharsets.map((l) => l.name)) ?? req.backend.cn.charset.name; - const mediaTypeCandidate = negotiator.mediaType(availableMediaTypes.map((l) => l.name)) ?? req.backend.cn.mediaType.name; - - req.cn = { - language: req.backend.app.languages.get(languageCandidate), - mediaType: req.backend.app.mediaTypes.get(mediaTypeCandidate), - charset: req.backend.app.charsets.get(charsetCandidate), - }; - - return req; -}; diff --git a/packages/servers/http/src/decorators/backend/index.ts b/packages/servers/http/src/decorators/backend/index.ts deleted file mode 100644 index b51acdd..0000000 --- a/packages/servers/http/src/decorators/backend/index.ts +++ /dev/null @@ -1,18 +0,0 @@ -import {BackendState, ParamRequestDecorator} from '@modal-sh/yasumi/backend'; -import {decorateRequestWithContentNegotiation} from './content-negotiation'; -import {decorateRequestWithResource} from './resource'; - -declare module '@modal-sh/yasumi/backend' { - interface RequestContext { - backend: BackendState; - } -} - -export const decorateRequestWithBackend: ParamRequestDecorator<[BackendState]> = (backend) => (req) => { - req.backend = backend; - - decorateRequestWithContentNegotiation(req); - decorateRequestWithResource(req); - - return req; -}; diff --git a/packages/servers/http/src/decorators/backend/resource.ts b/packages/servers/http/src/decorators/backend/resource.ts deleted file mode 100644 index 63df76e..0000000 --- a/packages/servers/http/src/decorators/backend/resource.ts +++ /dev/null @@ -1,25 +0,0 @@ -import {Resource} from '@modal-sh/yasumi'; -import {RequestDecorator} from '@modal-sh/yasumi/backend'; - -declare module '@modal-sh/yasumi/backend' { - interface RequestContext { - resource?: Resource; - resourceId?: string; - } -} - -export const decorateRequestWithResource: RequestDecorator = (req) => { - const [, resourceRouteName, resourceId] = req.url?.split('/') ?? []; - const resource = Array.from(req.backend.app.resources) - .find((r) => r.state.routeName === resourceRouteName) as Resource | undefined; - - if (typeof resource !== 'undefined') { - req.backend.dataSource.prepareResource(resource); - req.resource = resource; - if (resourceId?.trim().length > 0) { - req.resourceId = resourceId; - } - } - - return req; -}; diff --git a/packages/servers/http/src/decorators/method/index.ts b/packages/servers/http/src/decorators/method/index.ts deleted file mode 100644 index 6a81adb..0000000 --- a/packages/servers/http/src/decorators/method/index.ts +++ /dev/null @@ -1,24 +0,0 @@ -import {RequestDecorator} from '@modal-sh/yasumi/backend'; - -const METHOD_SPOOF_HEADER_NAME = 'x-original-method' as const; -const METHOD_SPOOF_ORIGINAL_METHOD = 'POST' as const; -const WHITELISTED_METHODS = [ - 'QUERY' -] as const; - -export const decorateRequestWithMethod: RequestDecorator = (req) => { - req.method = req.method?.trim().toUpperCase() ?? ''; - if (req.method === METHOD_SPOOF_ORIGINAL_METHOD) { - const spoofedMethod = req.headers[METHOD_SPOOF_HEADER_NAME]; - if (Array.isArray(spoofedMethod)) { - return req; - } - const whitelistedMethod = WHITELISTED_METHODS.find((s) => s === spoofedMethod); - - if (typeof whitelistedMethod === 'string') { - req.method = whitelistedMethod; - } - } - - return req; -}; diff --git a/packages/servers/http/src/decorators/url/base-path.ts b/packages/servers/http/src/decorators/url/base-path.ts deleted file mode 100644 index b101f09..0000000 --- a/packages/servers/http/src/decorators/url/base-path.ts +++ /dev/null @@ -1,13 +0,0 @@ -import {ParamRequestDecorator} from '@modal-sh/yasumi/backend'; - -declare module '@modal-sh/yasumi/backend' { - interface RequestContext { - basePath: string; - } -} - -export const decorateRequestWithBasePath: ParamRequestDecorator<[string]> = (basePath) => (req) => { - req.basePath = basePath; - - return req; -} diff --git a/packages/servers/http/src/decorators/url/host.ts b/packages/servers/http/src/decorators/url/host.ts deleted file mode 100644 index 9901dca..0000000 --- a/packages/servers/http/src/decorators/url/host.ts +++ /dev/null @@ -1,13 +0,0 @@ -import {ParamRequestDecorator} from '@modal-sh/yasumi/backend'; - -declare module '@modal-sh/yasumi/backend' { - interface RequestContext { - host: string; - } -} - -export const decorateRequestWithHost: ParamRequestDecorator<[string]> = (host) => (req) => { - req.host = host; - - return req; -}; diff --git a/packages/servers/http/src/decorators/url/index.ts b/packages/servers/http/src/decorators/url/index.ts deleted file mode 100644 index 6d472cc..0000000 --- a/packages/servers/http/src/decorators/url/index.ts +++ /dev/null @@ -1,26 +0,0 @@ -import {ParamRequestDecorator} from '@modal-sh/yasumi/backend'; -import {CreateServerParams} from '../../core'; -import {decorateRequestWithScheme} from './scheme'; -import {decorateRequestWithHost} from './host'; -import {decorateRequestWithBasePath} from './base-path'; - -declare module '@modal-sh/yasumi/backend' { - interface RequestContext { - rawUrl?: string; - query: URLSearchParams; - } -} - -export const decorateRequestWithUrl: ParamRequestDecorator<[CreateServerParams]> = (serverParams) => (req) => { - const isHttps = 'key' in serverParams && 'cert' in serverParams; - decorateRequestWithScheme(isHttps ? 'https' : 'http')(req); - decorateRequestWithHost(serverParams.host ?? '127.0.0.1')(req); - decorateRequestWithBasePath(serverParams.basePath ?? '')(req); - const basePath = new URL(req.basePath, `${req.scheme}://${req.host}`); - const parsedUrl = new URL(`${basePath.pathname}/${req.url ?? ''}`, basePath.origin); - req.rawUrl = req.url; - req.url = req.url?.slice(basePath.pathname.length) ?? ''; - req.query = parsedUrl.searchParams; - - return req; -}; diff --git a/packages/servers/http/src/decorators/url/scheme.ts b/packages/servers/http/src/decorators/url/scheme.ts deleted file mode 100644 index cb02d60..0000000 --- a/packages/servers/http/src/decorators/url/scheme.ts +++ /dev/null @@ -1,13 +0,0 @@ -import {ParamRequestDecorator} from '@modal-sh/yasumi/backend'; - -declare module '@modal-sh/yasumi/backend' { - interface RequestContext { - scheme: string; - } -} - -export const decorateRequestWithScheme: ParamRequestDecorator<[string]> = (scheme) => (req) => { - req.scheme = scheme; - - return req; -}; diff --git a/packages/servers/http/src/handlers/default.ts b/packages/servers/http/src/handlers/default.ts deleted file mode 100644 index 657f2f5..0000000 --- a/packages/servers/http/src/handlers/default.ts +++ /dev/null @@ -1,78 +0,0 @@ -import {constants} from 'http2'; -import {AllowedMiddlewareSpecification, getAllowString, Middleware} from '@modal-sh/yasumi/backend'; -import {LinkMap} from '../utils'; -import {PlainResponse, ErrorPlainResponse} from '../response'; -import {getAcceptPatchString, getAcceptPostString} from '@modal-sh/yasumi'; - -export const handleGetRoot: Middleware = (req, res) => { - const { backend, basePath } = req; - - const data = { - name: backend.app.name - }; - - const registeredResources = Array.from(backend.app.resources); - const availableResources = registeredResources.filter((r) => ( - r.state.canFetchCollection - || r.state.canCreate - )); - - const headers: Record = {}; - if (availableResources.length > 0) { - // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Link - headers['Link'] = new LinkMap( - availableResources.map((r) => ({ - url: `${basePath}/${r.state.routeName}`, - params: { - rel: 'related', - name: r.state.routeName, - }, - })) - ) - .toString(); - } - - return new PlainResponse({ - headers, - statusMessage: 'ok', - statusCode: constants.HTTP_STATUS_OK, - body: data, - res, - }); -}; - -export const handleOptions = (middlewares: AllowedMiddlewareSpecification[]): Middleware => (req, res) => { - if (middlewares.length > 0) { - const allowString = getAllowString(middlewares); - const headers: Record = { - 'Allow': getAllowString(middlewares), - }; - - const allowedMethods = allowString.split(','); - if (allowedMethods.includes('POST')) { - headers['Accept-Post'] = getAcceptPostString(req.backend.app.mediaTypes); - } - - if (allowedMethods.includes('PATCH')) { - const validPatchTypes = Object.entries(req.resource.state.canPatch) - .filter(([, allowed]) => allowed); - - if (validPatchTypes.length > 0) { - headers['Accept-Patch'] = getAcceptPatchString(req.resource.state.canPatch); - } - } - - return new PlainResponse({ - headers, - statusMessage: 'provideOptions', - statusCode: constants.HTTP_STATUS_NO_CONTENT, - res, - }); - } - - // TODO add option for custom error handler - throw new ErrorPlainResponse('methodNotAllowed', { - statusCode: constants.HTTP_STATUS_METHOD_NOT_ALLOWED, - res, - }); -}; diff --git a/packages/servers/http/src/handlers/resource.ts b/packages/servers/http/src/handlers/resource.ts deleted file mode 100644 index 82abbe0..0000000 --- a/packages/servers/http/src/handlers/resource.ts +++ /dev/null @@ -1,449 +0,0 @@ -import { constants } from 'http2'; -import assert from 'assert'; -import {Middleware} from '@modal-sh/yasumi/backend'; -import { - applyDelta, Query, - Delta, - PATCH_CONTENT_MAP_TYPE, - PatchContentType, - queryMediaTypes, - validation as v, -} from '@modal-sh/yasumi'; -import {ErrorPlainResponse, PlainResponse} from '../response'; - -// TODO add handleQueryCollection() - -export const handleQueryCollection: Middleware = async (req, res) => { - const { - body, - resource, - backend, - } = req; - - let data: v.Output[]; - let totalItemCount: number | undefined; - try { - // check which attributes have specifics on the queries (e.g. fuzzy search on strings) - const dataSourceQuery = body as Query; - data = await resource.dataSource.getMultiple(dataSourceQuery); // TODO paginated responses per resource - if (backend.showTotalItemCountOnGetCollection && typeof resource.dataSource.getTotalCount === 'function') { - totalItemCount = await resource.dataSource.getTotalCount(dataSourceQuery); - } - } catch (cause) { - throw new ErrorPlainResponse( - 'unableToFetchResourceCollection', - { - cause, - statusCode: constants.HTTP_STATUS_INTERNAL_SERVER_ERROR, - res, - } - ); - } - - const headers: Record = {}; - if (typeof totalItemCount !== 'undefined') { - headers['X-Resource-Total-Item-Count'] = totalItemCount.toString(); - } - - return new PlainResponse({ - headers, - statusCode: constants.HTTP_STATUS_OK, - statusMessage: 'resourceCollectionFetched', - body: data, - res, - }); -}; - -export const handleGetCollection: Middleware = async (req, res) => { - const { - // TODO don't turn query into URLSearchParams just yet - query, - resource, - backend, - } = req; - - let data: v.Output[]; - let totalItemCount: number | undefined; - try { - // check which attributes have specifics on the queries (e.g. fuzzy search on strings) - const dataSourceQuery = queryMediaTypes.applicationXWwwFormUrlencoded.deserialize( - query.toString(), - // TODO compute processEntry options based on resource attribute metadata (e.g. fulltext, queryable attributes - firstname, lastname, middlename) - ); - data = await resource.dataSource.getMultiple(dataSourceQuery); // TODO paginated responses per resource - if (backend.showTotalItemCountOnGetCollection && typeof resource.dataSource.getTotalCount === 'function') { - totalItemCount = await resource.dataSource.getTotalCount(dataSourceQuery); - } - } catch (cause) { - throw new ErrorPlainResponse( - 'unableToFetchResourceCollection', - { - cause, - statusCode: constants.HTTP_STATUS_INTERNAL_SERVER_ERROR, - res, - } - ); - } - - const headers: Record = {}; - if (typeof totalItemCount !== 'undefined') { - headers['X-Resource-Total-Item-Count'] = totalItemCount.toString(); - } - - return new PlainResponse({ - headers, - statusCode: constants.HTTP_STATUS_OK, - statusMessage: 'resourceCollectionFetched', - body: data, - res, - }); -}; - -const isResourceIdDefined = (resourceId?: string): resourceId is string => !( - typeof resourceId === 'undefined' || resourceId.trim().length < 1 -); - -export const handleGetItem: Middleware = async (req, res) => { - const { resource, resourceId } = req; - - assert( - isResourceIdDefined(resourceId), - new ErrorPlainResponse( - 'resourceIdNotGiven', - { - statusCode: constants.HTTP_STATUS_BAD_REQUEST, - res, - } - ) - ); - - let data: v.Output | null = null; - try { - data = await resource.dataSource.getById(resourceId); - } catch (cause) { - throw new ErrorPlainResponse( - 'unableToFetchResource', - { - cause, - statusCode: constants.HTTP_STATUS_INTERNAL_SERVER_ERROR, - res, - } - ); - } - - if (!(typeof data !== 'undefined' && data !== null)) { - throw new ErrorPlainResponse( - 'resourceNotFound', - { - statusCode: constants.HTTP_STATUS_NOT_FOUND, - res, - }, - ); - } - - return new PlainResponse({ - statusCode: constants.HTTP_STATUS_OK, - statusMessage: 'resourceFetched', - body: data, - res, - }); -}; - -export const handleDeleteItem: Middleware = async (req, res) => { - const { resource, resourceId, backend } = req; - - assert( - isResourceIdDefined(resourceId), - new ErrorPlainResponse( - 'resourceIdNotGiven', - { - statusCode: constants.HTTP_STATUS_BAD_REQUEST, - res, - } - ) - ); - - let existing: unknown | null; - try { - existing = await resource.dataSource.getById(resourceId); - } catch (cause) { - throw new ErrorPlainResponse('unableToFetchResource', { - cause, - statusCode: constants.HTTP_STATUS_INTERNAL_SERVER_ERROR, - res, - }); - } - - if (!existing && backend!.throwsErrorOnDeletingNotFound) { - throw new ErrorPlainResponse('deleteNonExistingResource', { - statusCode: constants.HTTP_STATUS_NOT_FOUND, - res - }); - } - - try { - if (existing) { - // TODO should we still deal with the delete return? - await resource.dataSource.delete(resourceId); - } - } catch (cause) { - throw new ErrorPlainResponse('unableToDeleteResource', { - cause, - statusCode: constants.HTTP_STATUS_INTERNAL_SERVER_ERROR, - res - }) - } - - return new PlainResponse({ - statusCode: constants.HTTP_STATUS_NO_CONTENT, - statusMessage: 'resourceDeleted', - res, - }); -}; - -export const handlePatchItem: Middleware = async (req, res) => { - const { - resource, - resourceId, - body, - headers, - } = req; - - const idAttr = resource.state.shared.get('idAttr'); - assert( - isIdAttributeDefined(idAttr), - new ErrorPlainResponse('resourceIdNotGiven', { - statusCode: constants.HTTP_STATUS_INTERNAL_SERVER_ERROR, - res, - }) - ); - - assert( - isResourceIdDefined(resourceId), - new ErrorPlainResponse( - 'resourceIdNotGiven', - { - statusCode: constants.HTTP_STATUS_BAD_REQUEST, - res, - } - ) - ); - - let existing: unknown | null; - try { - existing = await resource.dataSource.getById(resourceId); - } catch (cause) { - throw new ErrorPlainResponse('unableToFetchResource', { - cause, - statusCode: constants.HTTP_STATUS_INTERNAL_SERVER_ERROR, - res, - }); - } - - if (!existing) { - throw new ErrorPlainResponse('patchNonExistingResource', { - statusCode: constants.HTTP_STATUS_NOT_FOUND, - res, - }); - } - - let newObject: v.Output | null; - const patchType = PATCH_CONTENT_MAP_TYPE[headers['content-type'] as PatchContentType]; - - switch (patchType) { - case 'merge': { - try { - newObject = await resource.dataSource.patch(resourceId, body as object); - } catch (cause) { - throw new ErrorPlainResponse('unableToPatchResource', { - cause, - statusCode: constants.HTTP_STATUS_INTERNAL_SERVER_ERROR, - res, - }); - } - break; - } - case 'delta': { - let modifiedObject: Record; - const { [idAttr]: id, ...theExisting } = existing as Record; - try { - modifiedObject = await applyDelta( - theExisting as Record, - body as Delta[], - resource.schema, - ); - } catch (cause) { - throw new ErrorPlainResponse('invalidResourcePatch', { - cause, - statusCode: constants.HTTP_STATUS_UNPROCESSABLE_ENTITY, - res, - }); - } - - try { - newObject = await resource.dataSource.patch(resourceId, { - ...modifiedObject, - [idAttr]: id, // TODO should ID belong to the resource? - }); - } catch (cause) { - throw new ErrorPlainResponse('unableToPatchResource', { - cause, - statusCode: constants.HTTP_STATUS_INTERNAL_SERVER_ERROR, - res, - }); - } - break; - } - default: - throw new ErrorPlainResponse('invalidResourcePatchType', { - statusCode: constants.HTTP_STATUS_BAD_REQUEST, - res, - }); - } - - return new PlainResponse({ - statusCode: constants.HTTP_STATUS_OK, - statusMessage: 'resourcePatched', - body: newObject, - res, - }); -}; - -const isIdAttributeDefined = (idAttr?: unknown): idAttr is string => ( - typeof idAttr !== 'undefined' -); - -export const handleCreateItem: Middleware = async (req, res) => { - const { resource, body, backend, basePath } = req; - - const idAttr = resource.state.shared.get('idAttr'); - assert( - isIdAttributeDefined(idAttr), - new ErrorPlainResponse('unableToGenerateIdFromResourceDataSource', { - statusCode: constants.HTTP_STATUS_INTERNAL_SERVER_ERROR, - res, - }) - ); - - let newId; - let params: v.Output; - try { - newId = await resource.dataSource.newId(); - params = { ...body as Record }; - params[idAttr] = newId; - } catch (cause) { - throw new ErrorPlainResponse('unableToAssignIdFromResourceDataSource', { - cause, - statusCode: constants.HTTP_STATUS_INTERNAL_SERVER_ERROR, - res, - }); - } - - const location = `${basePath}/${resource.state.routeName}/${newId}`; - res.emit('response', { - Location: location, - }); - // already return 202 accepted here - - let newObject; - let totalItemCount: number | undefined; - - try { - if (backend!.showTotalItemCountOnCreateItem && typeof resource.dataSource.getTotalCount === 'function') { - totalItemCount = await resource.dataSource.getTotalCount(); - totalItemCount += 1; - } - newObject = await resource.dataSource.create(params); - } catch (cause) { - throw new ErrorPlainResponse('unableToCreateResource', { - cause, - statusCode: constants.HTTP_STATUS_INTERNAL_SERVER_ERROR, - res, - }); - } - - if (typeof totalItemCount !== 'undefined') { - return new PlainResponse({ - statusCode: constants.HTTP_STATUS_CREATED, - headers: { - 'Location': location, - 'X-Resource-Total-Item-Count': totalItemCount.toString() - }, - body: newObject, - statusMessage: 'resourceCreated', - res, - }); - } - - return new PlainResponse({ - statusCode: constants.HTTP_STATUS_CREATED, - body: newObject, - headers: { - 'Location': location, - }, - statusMessage: 'resourceCreated', - res, - }); -} - -export const handleEmplaceItem: Middleware = async (req, res) => { - const { resource, resourceId, basePath, body, backend } = req; - - assert( - isResourceIdDefined(resourceId), - new ErrorPlainResponse( - 'resourceIdNotGiven', - { - statusCode: constants.HTTP_STATUS_BAD_REQUEST, - res, - } - ) - ); - - const idAttr = resource.state.shared.get('idAttr'); - assert( - isIdAttributeDefined(idAttr), - new ErrorPlainResponse('unableToGenerateIdFromResourceDataSource', { - statusCode: constants.HTTP_STATUS_INTERNAL_SERVER_ERROR, - res, - }) - ); - - let newObject: v.Output; - let isCreated: boolean; - try { - const params = { ...body as Record }; - params[idAttr] = resourceId; - [newObject, isCreated] = await resource.dataSource.emplace(resourceId, params); - } catch (cause) { - throw new ErrorPlainResponse('unableToEmplaceResource', { - cause, - statusCode: constants.HTTP_STATUS_INTERNAL_SERVER_ERROR, - res, - }); - } - - const headers: Record = {}; - let totalItemCount: number | undefined; - if (backend!.showTotalItemCountOnCreateItem && typeof resource.dataSource.getTotalCount === 'function') { - totalItemCount = await resource.dataSource.getTotalCount(); - } - if (isCreated) { - headers['Location'] = `${basePath}/${resource.state.routeName}/${resourceId}`; - if (typeof totalItemCount !== 'undefined') { - headers['X-Resource-Total-Item-Count'] = totalItemCount.toString(); - } - } - - return new PlainResponse({ - statusCode: isCreated ? constants.HTTP_STATUS_CREATED : constants.HTTP_STATUS_OK, - headers, - statusMessage: ( - isCreated - ? 'resourceCreated' - : 'resourceReplaced' - ), - body: newObject, - res, - }); -} diff --git a/packages/servers/http/src/index.ts b/packages/servers/http/src/index.ts deleted file mode 100644 index 9b080f4..0000000 --- a/packages/servers/http/src/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './core'; -export * from './response'; diff --git a/packages/servers/http/src/response.ts b/packages/servers/http/src/response.ts deleted file mode 100644 index 1c64c7f..0000000 --- a/packages/servers/http/src/response.ts +++ /dev/null @@ -1,45 +0,0 @@ -import {Language, LanguageStatusMessageMap} from '@modal-sh/yasumi'; -import {MiddlewareResponseError, Response} from '@modal-sh/yasumi/backend'; - -interface PlainResponseParams extends Response { - body?: T; - res: U; -} - -interface HttpMiddlewareErrorParams extends Omit, 'statusMessage'> { - cause?: unknown -} - -export class ErrorPlainResponse extends MiddlewareResponseError implements PlainResponseParams { - readonly body?: T; - readonly res: U; - - constructor(statusMessage: keyof Language['statusMessages'], params: HttpMiddlewareErrorParams) { - super(statusMessage, params); - this.body = params.body; - this.res = params.res; - this.res.emit('response', this); - this.res.emit('close'); - } -} - -export class PlainResponse implements Response { - readonly statusCode: Response['statusCode']; - - readonly statusMessage?: keyof LanguageStatusMessageMap; - - readonly headers: Response['headers']; - - readonly body?: T; - - constructor(args: PlainResponseParams) { - this.statusCode = args.statusCode; - this.statusMessage = args.statusMessage; - this.headers = args.headers; - this.body = args.body; - args.res.emit('response', this); - args.res.emit('close'); - } -} - -// TODO stream response diff --git a/packages/servers/http/src/utils.ts b/packages/servers/http/src/utils.ts deleted file mode 100644 index c204504..0000000 --- a/packages/servers/http/src/utils.ts +++ /dev/null @@ -1,48 +0,0 @@ -import {IncomingMessage} from 'http'; -import {PATCH_CONTENT_TYPES} from '@modal-sh/yasumi'; - -export const isTextMediaType = (mediaType: string) => ( - mediaType.startsWith('text/') - || [ - 'application/json', - 'application/xml', - 'application/x-www-form-urlencoded', - ...PATCH_CONTENT_TYPES, - ].includes(mediaType) -); - -export const getBody = ( - req: IncomingMessage, -) => new Promise((resolve, reject) => { - let body = Buffer.from(''); - req.on('data', (chunk) => { - body = Buffer.concat([body, chunk]); - }); - req.on('end', async () => { - resolve(body); - }); - - req.on('error', (err) => { - reject(err); - }) -}); - -interface LinkMapEntry { - url: string; - params: Record; -} - -export class LinkMap extends Set { - toString() { - const entries = Array.from(this.values()); - - return entries.map((e) => { - const params = Object.entries(e.params); - - return [ - `<${e.url}>`, - ...params.map(([key, value]) => `${encodeURIComponent(key)}="${encodeURIComponent(value)}"`) - ].join(';') - }).join(','); - } -} diff --git a/packages/servers/http/test/features/decorators.test.ts b/packages/servers/http/test/features/decorators.test.ts deleted file mode 100644 index 688055d..0000000 --- a/packages/servers/http/test/features/decorators.test.ts +++ /dev/null @@ -1,103 +0,0 @@ -import {describe, afterAll, beforeAll, it} from 'vitest'; -import {Application, application, resource, Resource, validation as v} from '@modal-sh/yasumi'; -import {Backend, DataSource, RequestContext} from '@modal-sh/yasumi/backend'; -import {httpExtender, HttpServer} from '../../src'; -import {createTestClient, DummyDataSource, dummyGenerationStrategy, TEST_LANGUAGE, TestClient} from '../utils'; - -const PORT = 3001; -const HOST = '127.0.0.1'; -const BASE_PATH = '/api'; -const ACCEPT = 'application/json'; -const ACCEPT_LANGUAGE = 'en'; -const ACCEPT_CHARSET = 'utf-8'; -const CONTENT_TYPE_CHARSET = 'utf-8'; -const CONTENT_TYPE = ACCEPT; - -describe('decorators', () => { - let Piano: Resource; - let app: Application; - let dataSource: DataSource; - let backend: Backend; - let server: HttpServer; - let client: TestClient; - - beforeAll(() => { - Piano = resource(v.object( - { - brand: v.string() - }, - v.never() - )) - .name('Piano' as const) - .route('pianos' as const) - .id('id' as const, { - generationStrategy: dummyGenerationStrategy, - serialize: (id) => id?.toString() ?? '0', - deserialize: (id) => Number.isFinite(Number(id)) ? Number(id) : 0, - schema: v.number(), - }); - - app = application({ - name: 'piano-service', - }) - .language(TEST_LANGUAGE) - .resource(Piano); - - dataSource = new DummyDataSource(); - - backend = app - .createBackend({ - dataSource, - }) - .use(httpExtender); - - server = backend.createServer('http' as const, { - basePath: BASE_PATH - }); - - client = createTestClient({ - host: HOST, - port: PORT, - }) - .acceptMediaType(ACCEPT) - .acceptLanguage(ACCEPT_LANGUAGE) - .acceptCharset(ACCEPT_CHARSET) - .contentType(CONTENT_TYPE) - .contentCharset(CONTENT_TYPE_CHARSET); - - return new Promise((resolve, reject) => { - server.on('error', (err) => { - reject(err); - }); - - server.on('listening', () => { - resolve(); - }); - - // TODO add .inject() method - server.listen({ - port: PORT - }); - }); - }); - - afterAll(() => new Promise((resolve, reject) => { - server.close((err) => { - if (err) { - reject(err); - } - - resolve(); - }); - })); - - it('decorates requests', () => { - server.requestDecorator((req) => { - const reqMut = req as unknown as Record; - reqMut['foo'] = 'bar'; - return reqMut as unknown as RequestContext; - }); - - // TODO how to make assertions here - }); -}); diff --git a/packages/servers/http/test/handlers/default.test.ts b/packages/servers/http/test/handlers/default.test.ts deleted file mode 100644 index ee98c42..0000000 --- a/packages/servers/http/test/handlers/default.test.ts +++ /dev/null @@ -1,604 +0,0 @@ -import { - beforeAll, - afterAll, - afterEach, - beforeEach, - describe, - expect, - it, - vi, -} from 'vitest'; -import {constants} from 'http2'; -import {Backend, DataSource} from '@modal-sh/yasumi/backend'; -import { - application, - resource, - validation as v, - Resource, - Application, -} from '@modal-sh/yasumi'; -import {createTestClient, DummyDataSource, dummyGenerationStrategy, TEST_LANGUAGE, TestClient} from '../utils'; -import {httpExtender, HttpServer} from '../../src'; - -const PORT = 3000; -const HOST = '127.0.0.1'; -const BASE_PATH = '/api'; -const ACCEPT = 'application/json'; -const ACCEPT_LANGUAGE = 'en'; -const ACCEPT_CHARSET = 'utf-8'; -const CONTENT_TYPE_CHARSET = 'utf-8'; -const CONTENT_TYPE = ACCEPT; - -const prepareStatusMessage = (s: string) => s.replace(/\$RESOURCE/g, 'Piano'); - -describe('happy path', () => { - let Piano: Resource; - let app: Application; - let dataSource: DataSource; - let backend: Backend; - let server: HttpServer; - let client: TestClient; - - beforeAll(() => { - Piano = resource(v.object( - { - brand: v.string() - }, - v.never() - )) - .name('Piano' as const) - .route('pianos' as const) - .id('id' as const, { - generationStrategy: dummyGenerationStrategy, - serialize: (id) => id?.toString() ?? '0', - deserialize: (id) => Number.isFinite(Number(id)) ? Number(id) : 0, - schema: v.number(), - }); - - app = application({ - name: 'piano-service', - }) - .language(TEST_LANGUAGE) - .resource(Piano); - - dataSource = new DummyDataSource(); - - backend = app - .createBackend({ - dataSource, - }) - .use(httpExtender); - - server = backend.createServer('http', { - basePath: BASE_PATH - }); - - client = createTestClient({ - host: HOST, - port: PORT, - }) - .acceptMediaType(ACCEPT) - .acceptLanguage(ACCEPT_LANGUAGE) - .acceptCharset(ACCEPT_CHARSET) - .contentType(CONTENT_TYPE) - .contentCharset(CONTENT_TYPE_CHARSET); - - return new Promise((resolve, reject) => { - server.on('error', (err) => { - reject(err); - }); - - server.on('listening', () => { - resolve(); - }); - - server.listen({ - port: PORT - }); - }); - }); - - afterAll(() => new Promise((resolve, reject) => { - server.close((err) => { - if (err) { - reject(err); - } - - resolve(); - }); - })); - - describe('querying collections', () => { - beforeEach(() => { - vi - .spyOn(DummyDataSource.prototype, 'getMultiple') - .mockResolvedValueOnce([] as never); - }); - - beforeEach(() => { - Piano.canFetchCollection(); - }); - - afterEach(() => { - Piano.canFetchCollection(false); - }); - - it('returns data', async () => { - // const [res, resData] = await client({ - // method: 'QUERY', - // path: `${BASE_PATH}/pianos`, - // headers: { - // 'content-type': 'application/x-www-form-urlencoded', - // }, - // body: 'foo=bar', - // }); - - const [res, resData] = await client({ - method: 'POST', - path: `${BASE_PATH}/pianos`, - headers: { - 'content-type': 'application/x-www-form-urlencoded', - 'x-original-method': 'QUERY', - }, - body: 'foo=bar', - }); - - expect(res).toHaveProperty('statusCode', constants.HTTP_STATUS_OK); - expect(res).toHaveProperty('statusMessage', prepareStatusMessage(TEST_LANGUAGE.statusMessages.resourceCollectionFetched)); - expect(res.headers).toHaveProperty('content-type', expect.stringContaining(ACCEPT)); - - if (typeof resData === 'undefined') { - expect.fail('Response body must be defined.'); - return; - } - - expect(resData).toEqual([]); - }); - }); - - describe('serving collections', () => { - beforeEach(() => { - vi - .spyOn(DummyDataSource.prototype, 'getMultiple') - .mockResolvedValueOnce([] as never); - }); - - beforeEach(() => { - Piano.canFetchCollection(); - }); - - afterEach(() => { - Piano.canFetchCollection(false); - }); - - it('returns data', async () => { - const [res, resData] = await client({ - method: 'GET', - path: `${BASE_PATH}/pianos`, - }); - - expect(res).toHaveProperty('statusCode', constants.HTTP_STATUS_OK); - expect(res).toHaveProperty('statusMessage', prepareStatusMessage(TEST_LANGUAGE.statusMessages.resourceCollectionFetched)); - expect(res.headers).toHaveProperty('content-type', expect.stringContaining(ACCEPT)); - - if (typeof resData === 'undefined') { - expect.fail('Response body must be defined.'); - return; - } - - expect(resData).toEqual([]); - }); - - it('returns data on HEAD method', async () => { - const [res] = await client({ - method: 'HEAD', - path: `${BASE_PATH}/pianos`, - }); - - expect(res).toHaveProperty('statusCode', constants.HTTP_STATUS_OK); - expect(res).toHaveProperty('statusMessage', prepareStatusMessage(TEST_LANGUAGE.statusMessages.resourceCollectionFetched)); - }); - - it('returns options', async () => { - const [res] = await client({ - method: 'OPTIONS', - path: `${BASE_PATH}/pianos`, - }); - - expect(res).toHaveProperty('statusCode', constants.HTTP_STATUS_NO_CONTENT); - expect(res).toHaveProperty('statusMessage', prepareStatusMessage(TEST_LANGUAGE.statusMessages.provideOptions)); - const allowedMethods = res.headers.allow?.split(',').map((s) => s.trim()) ?? []; - expect(allowedMethods).toContain('GET'); - expect(allowedMethods).toContain('HEAD'); - }); - }); - - describe('serving items', () => { - const existingResource = { - id: 1, - brand: 'Yamaha' - }; - - beforeEach(() => { - vi - .spyOn(DummyDataSource.prototype, 'getById') - .mockResolvedValueOnce(existingResource as never); - }); - - beforeEach(() => { - Piano.canFetchItem(); - }); - - afterEach(() => { - Piano.canFetchItem(false); - }); - - it('returns data', async () => { - const [res, resData] = await client({ - method: 'GET', - path: `${BASE_PATH}/pianos/${existingResource.id}`, - }); - - expect(res).toHaveProperty('statusCode', constants.HTTP_STATUS_OK); - expect(res).toHaveProperty('statusMessage', prepareStatusMessage(TEST_LANGUAGE.statusMessages.resourceFetched)); - expect(res.headers).toHaveProperty('content-type', expect.stringContaining(ACCEPT)); - if (typeof resData === 'undefined') { - expect.fail('Response body must be defined.'); - return; - } - - expect(resData).toEqual(existingResource); - }); - - it('returns data on HEAD method', async () => { - const [res] = await client({ - method: 'HEAD', - path: `${BASE_PATH}/pianos/${existingResource.id}`, - }); - - expect(res).toHaveProperty('statusCode', constants.HTTP_STATUS_OK); - expect(res).toHaveProperty('statusMessage', prepareStatusMessage(TEST_LANGUAGE.statusMessages.resourceFetched)); - }); - - it('returns options', async () => { - const [res] = await client({ - method: 'OPTIONS', - path: `${BASE_PATH}/pianos/${existingResource.id}`, - }); - - expect(res).toHaveProperty('statusCode', constants.HTTP_STATUS_NO_CONTENT); - expect(res).toHaveProperty('statusMessage', prepareStatusMessage(TEST_LANGUAGE.statusMessages.provideOptions)); - const allowedMethods = res.headers.allow?.split(',').map((s) => s.trim()) ?? []; - expect(allowedMethods).toContain('GET'); - expect(allowedMethods).toContain('HEAD'); - }); - }); - - describe('creating items', () => { - const newResourceData = { - brand: 'K. Kawai' - }; - - const responseData = { - id: 2, - ...newResourceData, - }; - - beforeEach(() => { - vi - .spyOn(DummyDataSource.prototype, 'newId') - .mockResolvedValueOnce(responseData.id as never); - }); - - beforeEach(() => { - vi - .spyOn(DummyDataSource.prototype, 'create') - .mockResolvedValueOnce(responseData as never); - }); - - beforeEach(() => { - Piano.canCreate(); - }); - - afterEach(() => { - Piano.canCreate(false); - }); - - it('returns data', async () => { - const [res, resData] = await client({ - path: `${BASE_PATH}/pianos`, - method: 'POST', - body: newResourceData, - }); - - expect(res).toHaveProperty('statusCode', constants.HTTP_STATUS_CREATED); - expect(res).toHaveProperty('statusMessage', prepareStatusMessage(TEST_LANGUAGE.statusMessages.resourceCreated)); - expect(res.headers).toHaveProperty('content-type', expect.stringContaining(ACCEPT)); - expect(res.headers).toHaveProperty('location', `${BASE_PATH}/pianos/2`); - - if (typeof resData === 'undefined') { - expect.fail('Response body must be defined.'); - return; - } - - expect(resData).toEqual({ - ...newResourceData, - id: 2 - }); - }); - - it('returns options', async () => { - const [res] = await client({ - method: 'OPTIONS', - path: `${BASE_PATH}/pianos`, - }); - - expect(res).toHaveProperty('statusCode', constants.HTTP_STATUS_NO_CONTENT); - expect(res).toHaveProperty('statusMessage', prepareStatusMessage(TEST_LANGUAGE.statusMessages.provideOptions)); - const allowedMethods = res.headers.allow?.split(',').map((s) => s.trim()) ?? []; - expect(allowedMethods).toContain('POST'); - }); - }); - - describe('patching items', () => { - const existingResource = { - id: 1, - brand: 'Yamaha' - }; - - const patchData = { - brand: 'K. Kawai' - }; - - beforeEach(() => { - vi - .spyOn(DummyDataSource.prototype, 'getById') - .mockResolvedValueOnce(existingResource as never); - }); - - beforeEach(() => { - vi - .spyOn(DummyDataSource.prototype, 'patch') - .mockResolvedValueOnce({ - ...existingResource, - ...patchData, - } as never); - }); - - beforeEach(() => { - Piano.canPatch(); - }); - - afterEach(() => { - Piano.canPatch(false); - }); - - it('returns options', async () => { - const [res] = await client({ - method: 'OPTIONS', - path: `${BASE_PATH}/pianos/${existingResource.id}`, - }); - - expect(res).toHaveProperty('statusCode', constants.HTTP_STATUS_NO_CONTENT); - expect(res).toHaveProperty('statusMessage', prepareStatusMessage(TEST_LANGUAGE.statusMessages.provideOptions)); - const allowedMethods = res.headers.allow?.split(',').map((s) => s.trim()) ?? []; - expect(allowedMethods).toContain('PATCH'); - const acceptPatch = res.headers['accept-patch']?.split(',').map((s) => s.trim()) ?? []; - expect(acceptPatch).toContain('application/json-patch+json'); - expect(acceptPatch).toContain('application/merge-patch+json'); - }); - - describe('on merge', () => { - beforeEach(() => { - Piano.canPatch(false).canPatch(['merge']); - }); - - it('returns data', async () => { - const [res, resData] = await client({ - method: 'PATCH', - path: `${BASE_PATH}/pianos/${existingResource.id}`, - body: patchData, - headers: { - 'content-type': 'application/merge-patch+json', - }, - }); - - expect(res).toHaveProperty('statusCode', constants.HTTP_STATUS_OK); - expect(res).toHaveProperty('statusMessage', prepareStatusMessage(TEST_LANGUAGE.statusMessages.resourcePatched)); - expect(res.headers).toHaveProperty('content-type', expect.stringContaining(ACCEPT)); - - if (typeof resData === 'undefined') { - expect.fail('Response body must be defined.'); - return; - } - - expect(resData).toEqual({ - ...existingResource, - ...patchData, - }); - }); - }); - - describe('on delta', () => { - beforeEach(() => { - Piano.canPatch(false).canPatch(['delta']); - }); - - it('returns data', async () => { - const [res, resData] = await client({ - method: 'PATCH', - path: `${BASE_PATH}/pianos/${existingResource.id}`, - body: [ - { - op: 'replace', - path: 'brand', - value: patchData.brand, - }, - ], - headers: { - 'content-type': 'application/json-patch+json', - }, - }); - - expect(res).toHaveProperty('statusCode', constants.HTTP_STATUS_OK); - expect(res).toHaveProperty('statusMessage', prepareStatusMessage(TEST_LANGUAGE.statusMessages.resourcePatched)); - expect(res.headers).toHaveProperty('content-type', expect.stringContaining(ACCEPT)); - - if (typeof resData === 'undefined') { - expect.fail('Response body must be defined.'); - return; - } - - expect(resData).toEqual({ - ...existingResource, - ...patchData, - }); - }); - }); - }); - - describe('emplacing items', () => { - const existingResource = { - id: 1, - brand: 'Yamaha' - }; - - const emplaceResourceData = { - id: 1, - brand: 'K. Kawai' - }; - - beforeEach(() => { - Piano.canEmplace(); - }); - - afterEach(() => { - Piano.canEmplace(false); - }); - - it('returns data for replacement', async () => { - vi - .spyOn(DummyDataSource.prototype, 'emplace') - .mockResolvedValueOnce([{ - ...existingResource, - ...emplaceResourceData, - }, false] as never); - - const [res, resData] = await client({ - method: 'PUT', - path: `${BASE_PATH}/pianos/${emplaceResourceData.id}`, - body: emplaceResourceData, - }); - - expect(res).toHaveProperty('statusCode', constants.HTTP_STATUS_OK); - expect(res).toHaveProperty('statusMessage', prepareStatusMessage(TEST_LANGUAGE.statusMessages.resourceReplaced)); - expect(res.headers).toHaveProperty('content-type', expect.stringContaining(ACCEPT)); - - if (typeof resData === 'undefined') { - expect.fail('Response body must be defined.'); - return; - } - - expect(resData).toEqual(emplaceResourceData); - }); - - it('returns data for creation', async () => { - const newId = 2; - - vi - .spyOn(DummyDataSource.prototype, 'emplace') - .mockResolvedValueOnce([{ - ...existingResource, - ...emplaceResourceData, - id: newId - }, true] as never); - - const [res, resData] = await client({ - method: 'PUT', - path: `${BASE_PATH}/pianos/${newId}`, - body: { - ...emplaceResourceData, - id: newId, - }, - }); - - expect(res).toHaveProperty('statusCode', constants.HTTP_STATUS_CREATED); - expect(res).toHaveProperty('statusMessage', prepareStatusMessage(TEST_LANGUAGE.statusMessages.resourceCreated)); - expect(res.headers).toHaveProperty('content-type', expect.stringContaining(ACCEPT)); - expect(res.headers).toHaveProperty('location', `${BASE_PATH}/pianos/${newId}`); - - if (typeof resData === 'undefined') { - expect.fail('Response body must be defined.'); - return; - } - - expect(resData).toEqual({ - ...emplaceResourceData, - id: newId, - }); - }); - - it('returns options', async () => { - const [res] = await client({ - method: 'OPTIONS', - path: `${BASE_PATH}/pianos/${existingResource.id}`, - }); - - expect(res).toHaveProperty('statusCode', constants.HTTP_STATUS_NO_CONTENT); - expect(res).toHaveProperty('statusMessage', prepareStatusMessage(TEST_LANGUAGE.statusMessages.provideOptions)); - const allowedMethods = res.headers.allow?.split(',').map((s) => s.trim()) ?? []; - expect(allowedMethods).toContain('PUT'); - }); - }); - - describe('deleting items', () => { - const existingResource = { - id: 1, - brand: 'Yamaha' - }; - - beforeEach(() => { - vi - .spyOn(DummyDataSource.prototype, 'getById') - .mockResolvedValueOnce(existingResource as never); - }); - - beforeEach(() => { - vi - .spyOn(DummyDataSource.prototype, 'delete') - .mockReturnValueOnce(Promise.resolve() as never); - }); - - beforeEach(() => { - Piano.canDelete(); - }); - - afterEach(() => { - Piano.canDelete(false); - }); - - it('responds', async () => { - const [res, resData] = await client({ - method: 'DELETE', - path: `${BASE_PATH}/pianos/${existingResource.id}`, - }); - - expect(res).toHaveProperty('statusCode', constants.HTTP_STATUS_NO_CONTENT); - expect(res).toHaveProperty('statusMessage', prepareStatusMessage(TEST_LANGUAGE.statusMessages.resourceDeleted)); - expect(res.headers).not.toHaveProperty('content-type'); - expect(resData).toBeUndefined(); - }); - - it('returns options', async () => { - const [res] = await client({ - method: 'OPTIONS', - path: `${BASE_PATH}/pianos/${existingResource.id}`, - }); - - expect(res).toHaveProperty('statusCode', constants.HTTP_STATUS_NO_CONTENT); - expect(res).toHaveProperty('statusMessage', prepareStatusMessage(TEST_LANGUAGE.statusMessages.provideOptions)); - const allowedMethods = res.headers.allow?.split(',').map((s) => s.trim()) ?? []; - expect(allowedMethods).toContain('DELETE'); - }); - }); -}); diff --git a/packages/servers/http/test/handlers/error-handling.test.ts b/packages/servers/http/test/handlers/error-handling.test.ts deleted file mode 100644 index 017bbbf..0000000 --- a/packages/servers/http/test/handlers/error-handling.test.ts +++ /dev/null @@ -1,534 +0,0 @@ -import { - beforeAll, - afterAll, - afterEach, - beforeEach, - describe, - expect, - it, - vi, -} from 'vitest'; -import {constants} from 'http2'; -import {Backend, DataSource} from '@modal-sh/yasumi/backend'; -import {application, resource, validation as v, Resource, Application, Delta} from '@modal-sh/yasumi'; -import { - createTestClient, - TestClient, - DummyDataSource, - DummyError, - TEST_LANGUAGE, - dummyGenerationStrategy, -} from '../utils'; -import {httpExtender, HttpServer} from '../../src'; - -const PORT = 3001; -const HOST = '127.0.0.1'; -const BASE_PATH = '/api'; -const ACCEPT = 'application/json'; -const ACCEPT_LANGUAGE = 'en'; -const ACCEPT_CHARSET = 'utf-8'; -const CONTENT_TYPE_CHARSET = 'utf-8'; -const CONTENT_TYPE = ACCEPT; - -const prepareStatusMessage = (s: string) => s.replace(/\$RESOURCE/g, 'Piano'); - -describe('error handling', () => { - let Piano: Resource; - let app: Application; - let dataSource: DataSource; - let backend: Backend; - let server: HttpServer; - let client: TestClient; - - beforeAll(() => { - Piano = resource(v.object( - { - brand: v.string() - }, - v.never() - )) - .name('Piano' as const) - .route('pianos' as const) - .id('id' as const, { - generationStrategy: dummyGenerationStrategy, - serialize: (id) => id?.toString() ?? '0', - deserialize: (id) => Number.isFinite(Number(id)) ? Number(id) : 0, - schema: v.number(), - }); - - app = application({ - name: 'piano-service', - }) - .language(TEST_LANGUAGE) - .resource(Piano); - - dataSource = new DummyDataSource(); - - backend = app - .createBackend({ - dataSource, - }) - .use(httpExtender); - - server = backend.createServer('http', { - basePath: BASE_PATH - }); - - client = createTestClient({ - host: HOST, - port: PORT, - }) - .acceptMediaType(ACCEPT) - .acceptLanguage(ACCEPT_LANGUAGE) - .acceptCharset(ACCEPT_CHARSET) - .contentType(CONTENT_TYPE) - .contentCharset(CONTENT_TYPE_CHARSET); - - return new Promise((resolve, reject) => { - server.on('error', (err) => { - reject(err); - }); - - server.on('listening', () => { - resolve(); - }); - - server.listen({ - port: PORT - }); - }); - }); - - afterAll(() => new Promise((resolve, reject) => { - server.close((err) => { - if (err) { - reject(err); - } - - resolve(); - }); - })); - - describe('serving collections', () => { - beforeEach(() => { - Piano.canFetchCollection(); - }); - - afterEach(() => { - Piano.canFetchCollection(false); - }); - - it('throws on query', async () => { - vi - .spyOn(DummyDataSource.prototype, 'getMultiple') - .mockImplementationOnce(() => { throw new DummyError() }); - - const [res] = await client({ - method: 'GET', - path: `${BASE_PATH}/pianos`, - }); - - expect(res).toHaveProperty('statusCode', constants.HTTP_STATUS_INTERNAL_SERVER_ERROR); - expect(res).toHaveProperty('statusMessage', prepareStatusMessage(TEST_LANGUAGE.statusMessages.unableToFetchResourceCollection)); - }); - - it('throws on HEAD method', async () => { - vi - .spyOn(DummyDataSource.prototype, 'getMultiple') - .mockImplementationOnce(() => { throw new DummyError() }); - - const [res] = await client({ - method: 'HEAD', - path: `${BASE_PATH}/pianos`, - }); - - expect(res).toHaveProperty('statusCode', constants.HTTP_STATUS_INTERNAL_SERVER_ERROR); - expect(res).toHaveProperty('statusMessage', prepareStatusMessage(TEST_LANGUAGE.statusMessages.unableToFetchResourceCollection)); - }); - }); - - describe('serving items', () => { - beforeEach(() => { - Piano.canFetchItem(); - }); - - afterEach(() => { - Piano.canFetchItem(false); - }); - - it('throws on query', async () => { - vi - .spyOn(DummyDataSource.prototype, 'getById') - .mockImplementationOnce(() => { throw new DummyError() }); - - const [res] = await client({ - method: 'GET', - path: `${BASE_PATH}/pianos/2`, - }); - - expect(res).toHaveProperty('statusCode', constants.HTTP_STATUS_INTERNAL_SERVER_ERROR); - expect(res).toHaveProperty('statusMessage', prepareStatusMessage(TEST_LANGUAGE.statusMessages.unableToFetchResource)); - }); - - it('throws on HEAD method', async () => { - vi - .spyOn(DummyDataSource.prototype, 'getById') - .mockImplementationOnce(() => { throw new DummyError() }); - - const [res] = await client({ - method: 'HEAD', - path: `${BASE_PATH}/pianos/2`, - }); - - expect(res).toHaveProperty('statusCode', constants.HTTP_STATUS_INTERNAL_SERVER_ERROR); - expect(res).toHaveProperty('statusMessage', prepareStatusMessage(TEST_LANGUAGE.statusMessages.unableToFetchResource)); - }); - - it('throws on item not found', async () => { - vi - .spyOn(DummyDataSource.prototype, 'getById') - .mockResolvedValueOnce(null as never); - - const [res] = await client({ - method: 'GET', - path: `${BASE_PATH}/pianos/2`, - }); - - expect(res).toHaveProperty('statusCode', constants.HTTP_STATUS_NOT_FOUND); - }); - - it('throws on item not found on HEAD method', async () => { - const getById = vi.spyOn(DummyDataSource.prototype, 'getById'); - getById.mockResolvedValueOnce(null as never); - - const [res] = await client({ - method: 'HEAD', - path: `${BASE_PATH}/pianos/2`, - }); - - expect(res).toHaveProperty('statusCode', constants.HTTP_STATUS_NOT_FOUND); - }); - }); - - describe('creating items', () => { - const newData = { - brand: 'K. Kawai' - }; - - const existingResource = { - ...newData, - id: 1, - }; - - beforeEach(() => { - Piano.canCreate(); - }); - - afterEach(() => { - Piano.canCreate(false); - }); - - it('throws on error assigning ID', async () => { - vi - .spyOn(DummyDataSource.prototype, 'newId') - .mockImplementationOnce(() => { throw new DummyError() }); - - const [res] = await client({ - method: 'POST', - path: `${BASE_PATH}/pianos`, - body: newData, - }); - - expect(res).toHaveProperty('statusCode', constants.HTTP_STATUS_INTERNAL_SERVER_ERROR); - expect(res).toHaveProperty('statusMessage', prepareStatusMessage(TEST_LANGUAGE.statusMessages.unableToAssignIdFromResourceDataSource)); - }); - - it('throws on error creating resource', async () => { - vi - .spyOn(DummyDataSource.prototype, 'newId') - .mockResolvedValueOnce(existingResource.id as never); - - vi - .spyOn(DummyDataSource.prototype, 'create') - .mockImplementationOnce(() => { throw new DummyError() }); - - const [res] = await client({ - method: 'POST', - path: `${BASE_PATH}/pianos`, - body: newData, - }); - - expect(res).toHaveProperty('statusCode', constants.HTTP_STATUS_INTERNAL_SERVER_ERROR); - expect(res).toHaveProperty('statusMessage', prepareStatusMessage(TEST_LANGUAGE.statusMessages.unableToCreateResource)); - }); - }); - - describe('patching items', () => { - const existingResource = { - id: 1, - brand: 'Yamaha' - }; - - const newData = { - brand: 'K. Kawai' - }; - - // TODO add more tests - - it('throws on unable to fetch existing item', async () => { - Piano.canPatch(); - vi - .spyOn(DummyDataSource.prototype, 'getById') - .mockImplementationOnce(() => { throw new DummyError() }); - - const [res] = await client({ - method: 'PATCH', - path: `${BASE_PATH}/pianos/${existingResource.id}`, - body: newData, - headers: { - 'content-type': 'application/merge-patch+json', - }, - }); - - expect(res).toHaveProperty('statusCode', constants.HTTP_STATUS_INTERNAL_SERVER_ERROR); - expect(res).toHaveProperty('statusMessage', prepareStatusMessage(TEST_LANGUAGE.statusMessages.unableToFetchResource)); - Piano.canPatch(false); - }); - - it('throws on item to patch not found', async () => { - Piano.canPatch(); - vi - .spyOn(DummyDataSource.prototype, 'getById') - .mockResolvedValueOnce(null as never); - - const [res] = await client({ - method: 'PATCH', - path: `${BASE_PATH}/pianos/${existingResource.id}`, - body: newData, - headers: { - 'content-type': 'application/merge-patch+json', - }, - }); - - expect(res).toHaveProperty('statusCode', constants.HTTP_STATUS_NOT_FOUND); - expect(res).toHaveProperty('statusMessage', prepareStatusMessage(TEST_LANGUAGE.statusMessages.patchNonExistingResource)); - Piano.canPatch(false); - }); - - describe('on merge patch', () => { - const newMergeData = { - brand: 'K. Kawai' - }; - - beforeEach(() => { - Piano.canPatch(['merge']); - }); - - afterEach(() => { - Piano.canPatch(false); - }); - - it('throws on attempting to request a delta patch', async () => { - const [res] = await client({ - method: 'PATCH', - path: `${BASE_PATH}/pianos/${existingResource.id}`, - body: newMergeData, - headers: { - 'content-type': 'application/json-patch+json', - }, - }); - - expect(res).toHaveProperty('statusCode', constants.HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE); - expect(res).toHaveProperty('statusMessage', prepareStatusMessage(TEST_LANGUAGE.statusMessages.invalidResourcePatchType)); - }); - }); - - describe('on delta patch', () => { - beforeEach(() => { - Piano.canPatch(['delta']); - }); - - afterEach(() => { - Piano.canPatch(false); - }); - - it('throws on attempting to request a merge patch', async () => { - const [res] = await client({ - method: 'PATCH', - path: `${BASE_PATH}/pianos/${existingResource.id}`, - body: { brand: 'Hello' }, - headers: { - 'content-type': 'application/merge-patch+json', - }, - }); - - expect(res).toHaveProperty('statusCode', constants.HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE); - expect(res).toHaveProperty('statusMessage', prepareStatusMessage(TEST_LANGUAGE.statusMessages.invalidResourcePatchType)); - }); - - it('throws on operating with a delta to an attribute outside the schema', async () => { - const [res] = await client({ - method: 'PATCH', - path: `${BASE_PATH}/pianos/${existingResource.id}`, - body: [ - { - op: 'replace', - path: 'brandUnknown', - value: 'K. Kawai', - }, - ] satisfies Delta[], - headers: { - 'content-type': 'application/json-patch+json', - }, - }); - - expect(res).toHaveProperty('statusCode', constants.HTTP_STATUS_UNPROCESSABLE_ENTITY); - expect(res).toHaveProperty('statusMessage', prepareStatusMessage(TEST_LANGUAGE.statusMessages.invalidResourcePatch)); - }); - - it('throws on operating a delta with mismatched value type', async () => { - const [res] = await client({ - method: 'PATCH', - path: `${BASE_PATH}/pianos/${existingResource.id}`, - body: [ - { - op: 'replace', - path: 'brand', - value: 5, - }, - ] satisfies Delta[], - headers: { - 'content-type': 'application/json-patch+json', - }, - }); - - expect(res).toHaveProperty('statusCode', constants.HTTP_STATUS_UNPROCESSABLE_ENTITY); - expect(res).toHaveProperty('statusMessage', prepareStatusMessage(TEST_LANGUAGE.statusMessages.invalidResourcePatch)); - }); - - it('throws on performing an invalid delta', async () => { - const [res] = await client({ - method: 'PATCH', - path: `${BASE_PATH}/pianos/${existingResource.id}`, - body: [ - { - op: 'add', - path: 'brand', - value: 5, - }, - ] satisfies Delta[], - headers: { - 'content-type': 'application/json-patch+json', - }, - }); - - expect(res).toHaveProperty('statusCode', constants.HTTP_STATUS_UNPROCESSABLE_ENTITY); - expect(res).toHaveProperty('statusMessage', prepareStatusMessage(TEST_LANGUAGE.statusMessages.invalidResourcePatch)); - }); - }); - }); - - describe('emplacing items', () => { - const existingResource = { - id: 1, - brand: 'Yamaha' - }; - - beforeEach(() => { - vi - .spyOn(DummyDataSource.prototype, 'getById') - .mockResolvedValueOnce(existingResource as never); - }); - - const newData = { - id: 1, - brand: 'K. Kawai' - }; - - beforeEach(() => { - Piano.canEmplace(); - }); - - afterEach(() => { - Piano.canEmplace(false); - }); - - it('throws on unable to emplace', async () => { - vi - .spyOn(DummyDataSource.prototype, 'emplace') - .mockImplementationOnce(() => { throw new DummyError() }); - - const [res] = await client({ - method: 'PUT', - path: `${BASE_PATH}/pianos/${existingResource.id}`, - body: newData, - }); - - expect(res).toHaveProperty('statusCode', constants.HTTP_STATUS_INTERNAL_SERVER_ERROR); - expect(res).toHaveProperty('statusMessage', prepareStatusMessage(TEST_LANGUAGE.statusMessages.unableToEmplaceResource)); - }); - }); - - describe('deleting items', () => { - const existingResource = { - id: 1, - brand: 'Yamaha' - }; - - beforeEach(() => { - Piano.canDelete(); - backend.throwsErrorOnDeletingNotFound(); - }); - - afterEach(() => { - Piano.canDelete(false); - backend.throwsErrorOnDeletingNotFound(false); - }); - - it('throws on unable to check if item exists', async () => { - vi - .spyOn(DummyDataSource.prototype, 'getById') - .mockImplementationOnce(() => { throw new DummyError() }); - - const [res] = await client({ - method: 'DELETE', - path: `${BASE_PATH}/pianos/2`, - }); - - expect(res).toHaveProperty('statusCode', constants.HTTP_STATUS_INTERNAL_SERVER_ERROR); - expect(res).toHaveProperty('statusMessage', prepareStatusMessage(TEST_LANGUAGE.statusMessages.unableToFetchResource)); - }); - - it('throws on item not found', async () => { - vi - .spyOn(DummyDataSource.prototype, 'getById') - .mockResolvedValueOnce(null as never); - - const [res] = await client({ - method: 'DELETE', - path: `${BASE_PATH}/pianos/${existingResource.id}`, - }); - - expect(res).toHaveProperty('statusCode', constants.HTTP_STATUS_NOT_FOUND); - expect(res).toHaveProperty('statusMessage', prepareStatusMessage(TEST_LANGUAGE.statusMessages.deleteNonExistingResource)); - }); - - it('throws on unable to delete item', async () => { - vi - .spyOn(DummyDataSource.prototype, 'getById') - .mockResolvedValueOnce(existingResource as never); - - vi - .spyOn(DummyDataSource.prototype, 'delete') - .mockImplementationOnce(() => { throw new DummyError() }); - - const [res] = await client({ - method: 'DELETE', - path: `${BASE_PATH}/pianos/${existingResource.id}`, - }); - - expect(res).toHaveProperty('statusCode', constants.HTTP_STATUS_INTERNAL_SERVER_ERROR); - expect(res).toHaveProperty('statusMessage', prepareStatusMessage(TEST_LANGUAGE.statusMessages.unableToDeleteResource)); - }); - }); -}); diff --git a/packages/servers/http/test/utils.ts b/packages/servers/http/test/utils.ts deleted file mode 100644 index b1a5f0a..0000000 --- a/packages/servers/http/test/utils.ts +++ /dev/null @@ -1,480 +0,0 @@ -import {IncomingHttpHeaders, IncomingMessage, OutgoingHttpHeaders, request, RequestOptions} from 'http'; -import {Method, DataSource} from '@modal-sh/yasumi/backend'; -import {FALLBACK_LANGUAGE, Language} from '@modal-sh/yasumi'; - -interface ClientParams { - method: Method; - path: string; - headers?: IncomingHttpHeaders; - body?: unknown; -} - -type ResponseBody = Buffer | string | object; - -export interface TestClient { - (params: ClientParams): Promise<[IncomingMessage, ResponseBody?]>; - acceptMediaType(mediaType: string): this; - acceptLanguage(language: string): this; - acceptCharset(charset: string): this; - contentType(mediaType: string): this; - contentCharset(charset: string): this; -} - -export const createTestClient = (options: Omit): TestClient => { - const additionalHeaders: OutgoingHttpHeaders = {}; - const client = (params: ClientParams) => new Promise<[IncomingMessage, ResponseBody?]>((resolve, reject) => { - const { - ...etcAdditionalHeaders - } = additionalHeaders; - - // odd that request() uses OutgoingHttpHeaders instead of IncomingHttpHeaders... - const headers: OutgoingHttpHeaders = { - ...(options.headers ?? {}), - ...etcAdditionalHeaders, - ...(params.headers ?? {}), - }; - - let contentTypeHeader: string | undefined; - if (typeof params.body !== 'undefined') { - contentTypeHeader = headers['content-type'] = params.headers?.['content-type'] ?? 'application/json'; - } - - const req = request({ - ...options, - method: params.method, - path: params.path, - headers, - }); - - req.on('response', (res) => { - // if (req.method.toUpperCase() === 'QUERY') { - // res.statusMessage = ''; - // res.statusCode = 200; - // } - - res.on('error', (err) => { - reject(err); - }); - - let resBuffer: Buffer | undefined; - res.on('data', (c) => { - resBuffer = ( - typeof resBuffer === 'undefined' - ? Buffer.from(c) - : Buffer.concat([resBuffer, c]) - ); - }); - - res.on('close', () => { - const acceptHeader = Array.isArray(headers['accept']) ? headers['accept'].join('; ') : headers['accept']; - const contentTypeBase = acceptHeader ?? 'application/octet-stream'; - const [type, subtype] = contentTypeBase.split('/'); - const allSubtypes = subtype.split('+'); - if (typeof resBuffer !== 'undefined') { - if (allSubtypes.includes('json')) { - const acceptCharset = ( - Array.isArray(headers['accept-charset']) - ? headers['accept-charset'].join('; ') - : headers['accept-charset'] - ) as BufferEncoding | undefined; - resolve([res, JSON.parse(resBuffer.toString(acceptCharset ?? 'utf-8'))]); - return; - } - - if (type === 'text') { - const acceptCharset = ( - Array.isArray(headers['accept-charset']) - ? headers['accept-charset'].join('; ') - : headers['accept-charset'] - ) as BufferEncoding | undefined; - resolve([res, resBuffer.toString(acceptCharset ?? 'utf-8')]); - return; - } - - resolve([res, resBuffer]); - return; - } - - resolve([res]); - }); - }); - - req.on('error', (err) => { - reject(err); - }) - - if (typeof params.body !== 'undefined') { - const theContentTypeHeader = Array.isArray(contentTypeHeader) ? contentTypeHeader.join('; ') : contentTypeHeader?.toString(); - const contentTypeAll = theContentTypeHeader ?? 'application/octet-stream'; - const [contentTypeBase, ...contentTypeParams] = contentTypeAll.split(';').map((s) => s.replace(/\s+/g, '').trim()); - const charsetParam = contentTypeParams.find((s) => s.startsWith('charset=')); - const charset = charsetParam?.split('=')?.[1] as BufferEncoding | undefined; - const [, subtype] = contentTypeBase.split('/'); - const allSubtypes = subtype.split('+'); - req.write( - allSubtypes.includes('json') - ? JSON.stringify(params.body) - : Buffer.from(params.body?.toString() ?? '', contentTypeBase === 'text' ? charset : undefined) - ); - } - - req.end(); - }); - - client.acceptMediaType = function acceptMediaType(mediaType: string) { - additionalHeaders['accept'] = mediaType; - return this; - }; - - client.acceptLanguage = function acceptLanguage(language: string) { - additionalHeaders['accept-language'] = language; - return this; - }; - - client.acceptCharset = function acceptCharset(charset: string) { - additionalHeaders['accept-charset'] = charset; - return this; - }; - - client.contentType = function contentType(mediaType: string) { - additionalHeaders['content-type'] = mediaType; - return this; - }; - - client.contentCharset = function contentCharset(charset: string) { - additionalHeaders['content-type'] = `${additionalHeaders['content-type']}; charset="${charset}"`; - return this; - }; - - return client; -}; - -export const dummyGenerationStrategy = () => Promise.resolve(); - -export class DummyError extends Error {} - -export class DummyDataSource implements DataSource { - private resource?: { dataSource?: unknown }; - - async create(): Promise { - return {}; - } - - async delete(): Promise {} - - async emplace(): Promise<[object, boolean]> { - return [{}, false]; - } - - async getById(): Promise { - return {}; - } - - async newId(): Promise { - return ''; - } - - async getMultiple(): Promise { - return []; - } - - async getSingle(): Promise { - return {}; - } - - async getTotalCount(): Promise { - return 0; - } - - async initialize(): Promise {} - - async patch(): Promise { - return {}; - } - - prepareResource(rr: unknown) { - this.resource = rr as unknown as { dataSource: DummyDataSource }; - this.resource.dataSource = this; - } -} - -export const TEST_LANGUAGE: Language = { - name: FALLBACK_LANGUAGE.name, - statusMessages: { - resourceCollectionQueried: '$Resource Collection Queried', - unableToSerializeResponse: 'Unable To Serialize Response', - unableToEncodeResponse: 'Unable To Encode Response', - unableToBindResourceDataSource: 'Unable To Bind $RESOURCE Data Source', - unableToInitializeResourceDataSource: 'Unable To Initialize $RESOURCE Data Source', - unableToFetchResourceCollection: 'Unable To Fetch $RESOURCE Collection', - unableToFetchResource: 'Unable To Fetch $RESOURCE', - unableToDeleteResource: 'Unable To Delete $RESOURCE', - languageNotAcceptable: 'Language Not Acceptable', - characterSetNotAcceptable: 'Character Set Not Acceptable', - unableToDeserializeResource: 'Unable To Deserialize $RESOURCE', - unableToDecodeResource: 'Unable To Decode $RESOURCE', - mediaTypeNotAcceptable: 'Media Type Not Acceptable', - methodNotAllowed: 'Method Not Allowed', - urlNotFound: 'URL Not Found', - badRequest: 'Bad Request', - ok: 'OK', - provideOptions: 'Provide Options', - resourceCollectionFetched: '$RESOURCE Collection Fetched', - resourceFetched: '$RESOURCE Fetched', - resourceNotFound: '$RESOURCE Not Found', - deleteNonExistingResource: 'Delete Non-Existing $RESOURCE', - resourceDeleted: '$RESOURCE Deleted', - unableToDeserializeRequest: 'Unable To Deserialize Request', - patchNonExistingResource: 'Patch Non-Existing $RESOURCE', - unableToPatchResource: 'Unable To Patch $RESOURCE', - invalidResourcePatch: 'Invalid $RESOURCE Patch', - invalidResourcePatchType: 'Invalid $RESOURCE Patch Type', - invalidResource: 'Invalid $RESOURCE', - resourcePatched: '$RESOURCE Patched', - resourceCreated: '$RESOURCE Created', - resourceReplaced: '$RESOURCE Replaced', - unableToGenerateIdFromResourceDataSource: 'Unable To Generate ID From $RESOURCE Data Source', - unableToAssignIdFromResourceDataSource: 'Unable To Assign ID From $RESOURCE Data Source', - unableToEmplaceResource: 'Unable To Emplace $RESOURCE', - resourceIdNotGiven: '$RESOURCE ID Not Given', - unableToCreateResource: 'Unable To Create $RESOURCE', - notImplemented: 'Not Implemented', - internalServerError: 'Internal Server Error', - }, - bodies: { - badRequest: [ - 'An invalid request has been made.', - [ - 'Check if the request body has all the required attributes for this endpoint.', - 'Check if the request body has only the valid attributes for this endpoint.', - 'Check if the request body matches the schema for the resource associated with this endpoint.', - 'Check if the request is appropriate for this endpoint.', - ], - ], - languageNotAcceptable: [ - 'The server could not process a response suitable for the client\'s provided language requirement.', - [ - 'Choose from the available languages on this service.', - 'Contact the administrator to provide localization for the client\'s given requirements.', - ], - ], - characterSetNotAcceptable: [ - 'The server could not process a response suitable for the client\'s provided character set requirement.', - [ - 'Choose from the available character sets on this service.', - 'Contact the administrator to provide localization for the client\'s given requirements.', - ], - ], - mediaTypeNotAcceptable: [ - 'The server could not process a response suitable for the client\'s provided media type requirement.', - [ - 'Choose from the available media types on this service.', - 'Contact the administrator to provide localization for the client\'s given requirements.', - ], - ], - deleteNonExistingResource: [ - 'The client has attempted to delete a resource that does not exist.', - [ - 'Ensure that the resource still exists.', - 'Ensure that the correct method is provided.', - ], - ], - internalServerError: [ - 'An unknown error has occurred within the service.', - [ - 'Try the request again at a later time.', - 'Contact the administrator if the service remains in a degraded or non-functional state.', - ], - ], - invalidResource: [ - 'The request has an invalid structure or is missing some attributes.', - [ - 'Check if the request body has all the required attributes for this endpoint.', - 'Check if the request body has only the valid attributes for this endpoint.', - 'Check if the request body matches the schema for the resource associated with this endpoint.', - ], - ], - invalidResourcePatch: [ - 'The request has an invalid patch data.', - [ - 'Check if the appropriate patch type is specified on the request data.', - 'Check if the request body has all the required attributes for this endpoint.', - 'Check if the request body has only the valid attributes for this endpoint.', - 'Check if the request body matches the schema for the resource associated with this endpoint.', - ], - ], - invalidResourcePatchType: [ - 'The request has an invalid or unsupported kind of patch data.', - [ - 'Check if the appropriate patch type is specified on the request data.', - 'Check if the request body has all the required attributes for this endpoint.', - 'Check if the request body has only the valid attributes for this endpoint.', - 'Check if the request body matches the schema for the resource associated with this endpoint.', - ], - ], - methodNotAllowed: [ - 'A request with an invalid or unsupported method has been made.', - [ - 'Check if the request method is appropriate for this endpoint.', - 'Check if the client is authorized to perform the method on this endpoint.', - ] - ], - notImplemented: [ - 'The service does not have any implementation for the accessed endpoint.', - [ - 'Try the request again at a later time.', - 'Contact the administrator if the service remains in a degraded or non-functional state.', - ], - ], - patchNonExistingResource: [ - 'The client has attempted to patch a resource that does not exist.', - [ - 'Ensure that the resource still exists.', - 'Ensure that the correct method is provided.', - ], - ], - resourceIdNotGiven: [ - 'The resource ID is not provided for the accessed endpoint.', - [ - 'Check if the resource ID is provided and valid in the URL.', - 'Check if the request method is appropriate for this endpoint.', - ], - ], - unableToAssignIdFromResourceDataSource: [ - 'The resource could not be assigned an ID from the associated data source.', - [ - 'Try the request again at a later time.', - 'Contact the administrator regarding missing configuration or unavailability of dependencies.', - ], - ], - unableToBindResourceDataSource: [ - 'The resource could not be associated from the data source.', - [ - 'Try the request again at a later time.', - 'Contact the administrator regarding missing configuration or unavailability of dependencies.', - ], - ], - unableToCreateResource: [ - 'An error has occurred on creating the resource.', - [ - 'Check if the request method is appropriate for this endpoint.', - 'Check if the request body has all the required attributes for this endpoint.', - 'Check if the request body has only the valid attributes for this endpoint.', - 'Check if the request body matches the schema for the resource associated with this endpoint.', - 'Try the request again at a later time.', - 'Contact the administrator regarding missing configuration or unavailability of dependencies.', - ], - ], - unableToDecodeResource: [ - 'The resource byte array could not be decoded for the provided character set.', - [ - 'Choose from the available character sets on this service.', - 'Contact the administrator to provide localization for the client\'s given requirements.', - ], - ], - unableToDeleteResource: [ - 'An error has occurred on deleting the resource.', - [ - 'Check if the request method is appropriate for this endpoint.', - 'Try the request again at a later time.', - 'Contact the administrator regarding missing configuration or unavailability of dependencies.', - ], - ], - unableToDeserializeRequest: [ - 'The decoded request byte array could not be deserialized for the provided media type.', - [ - 'Choose from the available media types on this service.', - 'Contact the administrator to provide localization for the client\'s given requirements.', - ], - ], - unableToDeserializeResource: [ - 'The decoded resource could not be deserialized for the provided media type.', - [ - 'Choose from the available media types on this service.', - 'Contact the administrator to provide localization for the client\'s given requirements.', - ], - ], - unableToEmplaceResource: [ - 'An error has occurred on emplacing the resource.', - [ - 'Check if the request method is appropriate for this endpoint.', - 'Check if the request body has all the required attributes for this endpoint.', - 'Check if the request body has only the valid attributes for this endpoint.', - 'Check if the request body matches the schema for the resource associated with this endpoint.', - 'Try the request again at a later time.', - 'Contact the administrator regarding missing configuration or unavailability of dependencies.', - ], - ], - unableToEncodeResponse: [ - 'The response data could not be encoded for the provided character set.', - [ - 'Choose from the available character sets on this service.', - 'Contact the administrator to provide localization for the client\'s given requirements.', - ], - ], - unableToFetchResource: [ - 'An error has occurred on fetching the resource.', - [ - 'Check if the request method is appropriate for this endpoint.', - 'Try the request again at a later time.', - 'Contact the administrator regarding missing configuration or unavailability of dependencies.', - ], - ], - unableToFetchResourceCollection: [ - 'An error has occurred on fetching the resource collection.', - [ - 'Check if the request method is appropriate for this endpoint.', - 'Try the request again at a later time.', - 'Contact the administrator regarding missing configuration or unavailability of dependencies.', - ], - ], - unableToGenerateIdFromResourceDataSource: [ - 'The associated data source for the resource could not produce an ID.', - [ - 'Try the request again at a later time.', - 'Contact the administrator regarding missing configuration or unavailability of dependencies.', - ], - ], - unableToInitializeResourceDataSource: [ - 'The associated data source for the resource could not be connected for usage.', - [ - 'Try the request again at a later time.', - 'Contact the administrator regarding missing configuration or unavailability of dependencies.', - ], - ], - unableToPatchResource: [ - 'An error has occurred on patching the resource.', - [ - 'Check if the request method is appropriate for this endpoint.', - 'Check if the request body has all the required attributes for this endpoint.', - 'Check if the request body has only the valid attributes for this endpoint.', - 'Check if the request body matches the schema for the resource associated with this endpoint.', - 'Try the request again at a later time.', - 'Contact the administrator regarding missing configuration or unavailability of dependencies.', - ], - ], - unableToSerializeResponse: [ - 'The response data could not be serialized for the provided media type.', - [ - 'Choose from the available media types on this service.', - 'Contact the administrator to provide localization for the client\'s given requirements.', - ], - ], - urlNotFound: [ - 'An endpoint in the provided URL could not be found.', - [ - 'Check if the request URL is correct.', - 'Try the request again at a later time.', - 'Contact the administrator regarding missing configuration or unavailability of dependencies.', - ], - ], - resourceNotFound: [ - 'The resource in the provided URL could not be found.', - [ - 'Check if the request URL is correct.', - 'Try the request again at a later time.', - 'Contact the administrator regarding missing configuration or unavailability of dependencies.', - ], - ], - }, -}; diff --git a/packages/servers/http/tsconfig.json b/packages/servers/http/tsconfig.json deleted file mode 100644 index 74083d7..0000000 --- a/packages/servers/http/tsconfig.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "exclude": ["node_modules"], - "include": ["src", "types"], - "compilerOptions": { - "module": "ESNext", - "lib": ["ESNext"], - "importHelpers": true, - "declaration": true, - "sourceMap": true, - "rootDir": "./src", - "strict": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true, - "moduleResolution": "bundler", - "jsx": "react", - "esModuleInterop": true, - "target": "es2018", - "useDefineForClassFields": false, - "declarationMap": true - } -} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b5fd8a1..bb46509 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,165 +8,25 @@ importers: packages/core: dependencies: - tsx: - specifier: ^4.7.1 - version: 4.7.2 valibot: specifier: ^0.30.0 version: 0.30.0 devDependencies: - '@types/node': - specifier: ^20.11.30 - version: 20.12.7 - pridepack: - specifier: 2.6.0 - version: 2.6.0(tslib@2.6.2)(typescript@5.4.5) - tslib: - specifier: ^2.6.2 - version: 2.6.2 - typescript: - specifier: ^5.4.3 - version: 5.4.5 - vitest: - specifier: ^1.4.0 - version: 1.5.0(@types/node@20.12.7) - - packages/data-sources/duckdb: - dependencies: - '@modal-sh/yasumi': - specifier: workspace:* - version: link:../../core - duckdb-async: - specifier: ^0.10.0 - version: 0.10.0 - devDependencies: - '@types/node': - specifier: ^20.11.0 - version: 20.12.7 - pridepack: - specifier: 2.6.0 - version: 2.6.0(tslib@2.6.2)(typescript@5.4.5) - tslib: - specifier: ^2.6.2 - version: 2.6.2 - typescript: - specifier: ^5.3.3 - version: 5.4.5 - vitest: - specifier: ^1.2.0 - version: 1.5.0(@types/node@20.12.7) - - packages/data-sources/file-jsonl: - dependencies: - '@modal-sh/yasumi': - specifier: workspace:* - version: link:../../core - devDependencies: - '@types/node': - specifier: ^20.11.0 - version: 20.12.7 - pridepack: - specifier: 2.6.0 - version: 2.6.0(tslib@2.6.2)(typescript@5.4.5) - tslib: - specifier: ^2.6.2 - version: 2.6.2 - typescript: - specifier: ^5.3.3 - version: 5.4.5 - vitest: - specifier: ^1.2.0 - version: 1.5.0(@types/node@20.12.7) - - packages/examples/cms-web-api: - dependencies: - '@modal-sh/yasumi': - specifier: workspace:* - version: link:../../core - '@modal-sh/yasumi-data-source-file-jsonl': - specifier: workspace:* - version: link:../../data-sources/file-jsonl - '@modal-sh/yasumi-server-http': - specifier: workspace:* - version: link:../../servers/http - tsx: - specifier: ^4.7.1 - version: 4.7.2 - devDependencies: - '@types/node': - specifier: ^20.11.0 - version: 20.12.7 - pridepack: - specifier: 2.6.0 - version: 2.6.0(tslib@2.6.2)(typescript@5.4.5) - tslib: - specifier: ^2.6.2 - version: 2.6.2 - typescript: - specifier: ^5.3.3 - version: 5.4.5 - vitest: - specifier: ^1.2.0 - version: 1.5.0(@types/node@20.12.7) - - packages/examples/duckdb: - dependencies: - '@modal-sh/yasumi': - specifier: workspace:* - version: link:../../core - '@modal-sh/yasumi-data-source-duckdb': - specifier: workspace:* - version: link:../../data-sources/duckdb - '@modal-sh/yasumi-server-http': - specifier: workspace:* - version: link:../../servers/http - tsx: - specifier: ^4.7.1 - version: 4.7.2 - devDependencies: - '@types/node': - specifier: ^20.11.0 - version: 20.12.7 - pridepack: - specifier: 2.6.0 - version: 2.6.0(tslib@2.6.2)(typescript@5.4.5) - tslib: - specifier: ^2.6.2 - version: 2.6.2 - typescript: - specifier: ^5.3.3 - version: 5.4.5 - vitest: - specifier: ^1.2.0 - version: 1.5.0(@types/node@20.12.7) - - packages/servers/http: - dependencies: - '@modal-sh/yasumi': - specifier: workspace:* - version: link:../../core - negotiator: - specifier: ^0.6.3 - version: 0.6.3 - devDependencies: - '@types/negotiator': - specifier: ^0.6.3 - version: 0.6.3 '@types/node': specifier: ^20.11.0 - version: 20.12.7 + version: 20.11.0 pridepack: specifier: 2.6.0 - version: 2.6.0(tslib@2.6.2)(typescript@5.4.5) + version: 2.6.0(tslib@2.6.2)(typescript@5.3.3) tslib: specifier: ^2.6.2 version: 2.6.2 typescript: specifier: ^5.3.3 - version: 5.4.5 + version: 5.3.3 vitest: specifier: ^1.2.0 - version: 1.4.0(@types/node@20.12.7) + version: 1.2.0(@types/node@20.11.0) packages: @@ -176,6 +36,7 @@ packages: cpu: [ppc64] os: [aix] requiresBuild: true + dev: true optional: true /@esbuild/aix-ppc64@0.20.2: @@ -193,6 +54,7 @@ packages: cpu: [arm64] os: [android] requiresBuild: true + dev: true optional: true /@esbuild/android-arm64@0.20.2: @@ -210,6 +72,7 @@ packages: cpu: [arm] os: [android] requiresBuild: true + dev: true optional: true /@esbuild/android-arm@0.20.2: @@ -227,6 +90,7 @@ packages: cpu: [x64] os: [android] requiresBuild: true + dev: true optional: true /@esbuild/android-x64@0.20.2: @@ -244,6 +108,7 @@ packages: cpu: [arm64] os: [darwin] requiresBuild: true + dev: true optional: true /@esbuild/darwin-arm64@0.20.2: @@ -261,6 +126,7 @@ packages: cpu: [x64] os: [darwin] requiresBuild: true + dev: true optional: true /@esbuild/darwin-x64@0.20.2: @@ -278,6 +144,7 @@ packages: cpu: [arm64] os: [freebsd] requiresBuild: true + dev: true optional: true /@esbuild/freebsd-arm64@0.20.2: @@ -295,6 +162,7 @@ packages: cpu: [x64] os: [freebsd] requiresBuild: true + dev: true optional: true /@esbuild/freebsd-x64@0.20.2: @@ -312,6 +180,7 @@ packages: cpu: [arm64] os: [linux] requiresBuild: true + dev: true optional: true /@esbuild/linux-arm64@0.20.2: @@ -329,6 +198,7 @@ packages: cpu: [arm] os: [linux] requiresBuild: true + dev: true optional: true /@esbuild/linux-arm@0.20.2: @@ -346,6 +216,7 @@ packages: cpu: [ia32] os: [linux] requiresBuild: true + dev: true optional: true /@esbuild/linux-ia32@0.20.2: @@ -363,6 +234,7 @@ packages: cpu: [loong64] os: [linux] requiresBuild: true + dev: true optional: true /@esbuild/linux-loong64@0.20.2: @@ -380,6 +252,7 @@ packages: cpu: [mips64el] os: [linux] requiresBuild: true + dev: true optional: true /@esbuild/linux-mips64el@0.20.2: @@ -397,6 +270,7 @@ packages: cpu: [ppc64] os: [linux] requiresBuild: true + dev: true optional: true /@esbuild/linux-ppc64@0.20.2: @@ -414,6 +288,7 @@ packages: cpu: [riscv64] os: [linux] requiresBuild: true + dev: true optional: true /@esbuild/linux-riscv64@0.20.2: @@ -431,6 +306,7 @@ packages: cpu: [s390x] os: [linux] requiresBuild: true + dev: true optional: true /@esbuild/linux-s390x@0.20.2: @@ -448,6 +324,7 @@ packages: cpu: [x64] os: [linux] requiresBuild: true + dev: true optional: true /@esbuild/linux-x64@0.20.2: @@ -465,6 +342,7 @@ packages: cpu: [x64] os: [netbsd] requiresBuild: true + dev: true optional: true /@esbuild/netbsd-x64@0.20.2: @@ -482,6 +360,7 @@ packages: cpu: [x64] os: [openbsd] requiresBuild: true + dev: true optional: true /@esbuild/openbsd-x64@0.20.2: @@ -499,6 +378,7 @@ packages: cpu: [x64] os: [sunos] requiresBuild: true + dev: true optional: true /@esbuild/sunos-x64@0.20.2: @@ -516,6 +396,7 @@ packages: cpu: [arm64] os: [win32] requiresBuild: true + dev: true optional: true /@esbuild/win32-arm64@0.20.2: @@ -533,6 +414,7 @@ packages: cpu: [ia32] os: [win32] requiresBuild: true + dev: true optional: true /@esbuild/win32-ia32@0.20.2: @@ -550,6 +432,7 @@ packages: cpu: [x64] os: [win32] requiresBuild: true + dev: true optional: true /@esbuild/win32-x64@0.20.2: @@ -561,10 +444,6 @@ packages: dev: true optional: true - /@gar/promisify@1.1.3: - resolution: {integrity: sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==} - dev: false - /@jest/schemas@29.6.3: resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -576,168 +455,133 @@ packages: resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} dev: true - /@mapbox/node-pre-gyp@1.0.11: - resolution: {integrity: sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==} - hasBin: true - dependencies: - detect-libc: 2.0.3 - https-proxy-agent: 5.0.1 - make-dir: 3.1.0 - node-fetch: 2.7.0 - nopt: 5.0.0 - npmlog: 5.0.1 - rimraf: 3.0.2 - semver: 7.6.0 - tar: 6.2.1 - transitivePeerDependencies: - - encoding - - supports-color - dev: false - - /@npmcli/fs@2.1.2: - resolution: {integrity: sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - dependencies: - '@gar/promisify': 1.1.3 - semver: 7.6.0 - dev: false - - /@npmcli/move-file@2.0.1: - resolution: {integrity: sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - deprecated: This functionality has been moved to @npmcli/fs - dependencies: - mkdirp: 1.0.4 - rimraf: 3.0.2 - dev: false - /@ovyerus/licenses@6.4.4: resolution: {integrity: sha512-IHjc31WXciQT3hfvdY+M59jBkQp70Fpr04tNDVO5rez2PNv4u8tE6w//CkU+GeBoO9k2ahneSqzjzvlgjyjkGw==} engines: {node: '>=8'} dev: true - /@rollup/rollup-android-arm-eabi@4.14.3: - resolution: {integrity: sha512-X9alQ3XM6I9IlSlmC8ddAvMSyG1WuHk5oUnXGw+yUBs3BFoTizmG1La/Gr8fVJvDWAq+zlYTZ9DBgrlKRVY06g==} + /@rollup/rollup-android-arm-eabi@4.17.2: + resolution: {integrity: sha512-NM0jFxY8bB8QLkoKxIQeObCaDlJKewVlIEkuyYKm5An1tdVZ966w2+MPQ2l8LBZLjR+SgyV+nRkTIunzOYBMLQ==} cpu: [arm] os: [android] requiresBuild: true dev: true optional: true - /@rollup/rollup-android-arm64@4.14.3: - resolution: {integrity: sha512-eQK5JIi+POhFpzk+LnjKIy4Ks+pwJ+NXmPxOCSvOKSNRPONzKuUvWE+P9JxGZVxrtzm6BAYMaL50FFuPe0oWMQ==} + /@rollup/rollup-android-arm64@4.17.2: + resolution: {integrity: sha512-yeX/Usk7daNIVwkq2uGoq2BYJKZY1JfyLTaHO/jaiSwi/lsf8fTFoQW/n6IdAsx5tx+iotu2zCJwz8MxI6D/Bw==} cpu: [arm64] os: [android] requiresBuild: true dev: true optional: true - /@rollup/rollup-darwin-arm64@4.14.3: - resolution: {integrity: sha512-Od4vE6f6CTT53yM1jgcLqNfItTsLt5zE46fdPaEmeFHvPs5SjZYlLpHrSiHEKR1+HdRfxuzXHjDOIxQyC3ptBA==} + /@rollup/rollup-darwin-arm64@4.17.2: + resolution: {integrity: sha512-kcMLpE6uCwls023+kknm71ug7MZOrtXo+y5p/tsg6jltpDtgQY1Eq5sGfHcQfb+lfuKwhBmEURDga9N0ol4YPw==} cpu: [arm64] os: [darwin] requiresBuild: true dev: true optional: true - /@rollup/rollup-darwin-x64@4.14.3: - resolution: {integrity: sha512-0IMAO21axJeNIrvS9lSe/PGthc8ZUS+zC53O0VhF5gMxfmcKAP4ESkKOCwEi6u2asUrt4mQv2rjY8QseIEb1aw==} + /@rollup/rollup-darwin-x64@4.17.2: + resolution: {integrity: sha512-AtKwD0VEx0zWkL0ZjixEkp5tbNLzX+FCqGG1SvOu993HnSz4qDI6S4kGzubrEJAljpVkhRSlg5bzpV//E6ysTQ==} cpu: [x64] os: [darwin] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-arm-gnueabihf@4.14.3: - resolution: {integrity: sha512-ge2DC7tHRHa3caVEoSbPRJpq7azhG+xYsd6u2MEnJ6XzPSzQsTKyXvh6iWjXRf7Rt9ykIUWHtl0Uz3T6yXPpKw==} + /@rollup/rollup-linux-arm-gnueabihf@4.17.2: + resolution: {integrity: sha512-3reX2fUHqN7sffBNqmEyMQVj/CKhIHZd4y631duy0hZqI8Qoqf6lTtmAKvJFYa6bhU95B1D0WgzHkmTg33In0A==} cpu: [arm] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-arm-musleabihf@4.14.3: - resolution: {integrity: sha512-ljcuiDI4V3ySuc7eSk4lQ9wU8J8r8KrOUvB2U+TtK0TiW6OFDmJ+DdIjjwZHIw9CNxzbmXY39wwpzYuFDwNXuw==} + /@rollup/rollup-linux-arm-musleabihf@4.17.2: + resolution: {integrity: sha512-uSqpsp91mheRgw96xtyAGP9FW5ChctTFEoXP0r5FAzj/3ZRv3Uxjtc7taRQSaQM/q85KEKjKsZuiZM3GyUivRg==} cpu: [arm] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-arm64-gnu@4.14.3: - resolution: {integrity: sha512-Eci2us9VTHm1eSyn5/eEpaC7eP/mp5n46gTRB3Aar3BgSvDQGJZuicyq6TsH4HngNBgVqC5sDYxOzTExSU+NjA==} + /@rollup/rollup-linux-arm64-gnu@4.17.2: + resolution: {integrity: sha512-EMMPHkiCRtE8Wdk3Qhtciq6BndLtstqZIroHiiGzB3C5LDJmIZcSzVtLRbwuXuUft1Cnv+9fxuDtDxz3k3EW2A==} cpu: [arm64] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-arm64-musl@4.14.3: - resolution: {integrity: sha512-UrBoMLCq4E92/LCqlh+blpqMz5h1tJttPIniwUgOFJyjWI1qrtrDhhpHPuFxULlUmjFHfloWdixtDhSxJt5iKw==} + /@rollup/rollup-linux-arm64-musl@4.17.2: + resolution: {integrity: sha512-NMPylUUZ1i0z/xJUIx6VUhISZDRT+uTWpBcjdv0/zkp7b/bQDF+NfnfdzuTiB1G6HTodgoFa93hp0O1xl+/UbA==} cpu: [arm64] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-powerpc64le-gnu@4.14.3: - resolution: {integrity: sha512-5aRjvsS8q1nWN8AoRfrq5+9IflC3P1leMoy4r2WjXyFqf3qcqsxRCfxtZIV58tCxd+Yv7WELPcO9mY9aeQyAmw==} + /@rollup/rollup-linux-powerpc64le-gnu@4.17.2: + resolution: {integrity: sha512-T19My13y8uYXPw/L/k0JYaX1fJKFT/PWdXiHr8mTbXWxjVF1t+8Xl31DgBBvEKclw+1b00Chg0hxE2O7bTG7GQ==} cpu: [ppc64] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-riscv64-gnu@4.14.3: - resolution: {integrity: sha512-sk/Qh1j2/RJSX7FhEpJn8n0ndxy/uf0kI/9Zc4b1ELhqULVdTfN6HL31CDaTChiBAOgLcsJ1sgVZjWv8XNEsAQ==} + /@rollup/rollup-linux-riscv64-gnu@4.17.2: + resolution: {integrity: sha512-BOaNfthf3X3fOWAB+IJ9kxTgPmMqPPH5f5k2DcCsRrBIbWnaJCgX2ll77dV1TdSy9SaXTR5iDXRL8n7AnoP5cg==} cpu: [riscv64] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-s390x-gnu@4.14.3: - resolution: {integrity: sha512-jOO/PEaDitOmY9TgkxF/TQIjXySQe5KVYB57H/8LRP/ux0ZoO8cSHCX17asMSv3ruwslXW/TLBcxyaUzGRHcqg==} + /@rollup/rollup-linux-s390x-gnu@4.17.2: + resolution: {integrity: sha512-W0UP/x7bnn3xN2eYMql2T/+wpASLE5SjObXILTMPUBDB/Fg/FxC+gX4nvCfPBCbNhz51C+HcqQp2qQ4u25ok6g==} cpu: [s390x] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-x64-gnu@4.14.3: - resolution: {integrity: sha512-8ybV4Xjy59xLMyWo3GCfEGqtKV5M5gCSrZlxkPGvEPCGDLNla7v48S662HSGwRd6/2cSneMQWiv+QzcttLrrOA==} + /@rollup/rollup-linux-x64-gnu@4.17.2: + resolution: {integrity: sha512-Hy7pLwByUOuyaFC6mAr7m+oMC+V7qyifzs/nW2OJfC8H4hbCzOX07Ov0VFk/zP3kBsELWNFi7rJtgbKYsav9QQ==} cpu: [x64] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-x64-musl@4.14.3: - resolution: {integrity: sha512-s+xf1I46trOY10OqAtZ5Rm6lzHre/UiLA1J2uOhCFXWkbZrJRkYBPO6FhvGfHmdtQ3Bx793MNa7LvoWFAm93bg==} + /@rollup/rollup-linux-x64-musl@4.17.2: + resolution: {integrity: sha512-h1+yTWeYbRdAyJ/jMiVw0l6fOOm/0D1vNLui9iPuqgRGnXA0u21gAqOyB5iHjlM9MMfNOm9RHCQ7zLIzT0x11Q==} cpu: [x64] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-win32-arm64-msvc@4.14.3: - resolution: {integrity: sha512-+4h2WrGOYsOumDQ5S2sYNyhVfrue+9tc9XcLWLh+Kw3UOxAvrfOrSMFon60KspcDdytkNDh7K2Vs6eMaYImAZg==} + /@rollup/rollup-win32-arm64-msvc@4.17.2: + resolution: {integrity: sha512-tmdtXMfKAjy5+IQsVtDiCfqbynAQE/TQRpWdVataHmhMb9DCoJxp9vLcCBjEQWMiUYxO1QprH/HbY9ragCEFLA==} cpu: [arm64] os: [win32] requiresBuild: true dev: true optional: true - /@rollup/rollup-win32-ia32-msvc@4.14.3: - resolution: {integrity: sha512-T1l7y/bCeL/kUwh9OD4PQT4aM7Bq43vX05htPJJ46RTI4r5KNt6qJRzAfNfM+OYMNEVBWQzR2Gyk+FXLZfogGw==} + /@rollup/rollup-win32-ia32-msvc@4.17.2: + resolution: {integrity: sha512-7II/QCSTAHuE5vdZaQEwJq2ZACkBpQDOmQsE6D6XUbnBHW8IAhm4eTufL6msLJorzrHDFv3CF8oCA/hSIRuZeQ==} cpu: [ia32] os: [win32] requiresBuild: true dev: true optional: true - /@rollup/rollup-win32-x64-msvc@4.14.3: - resolution: {integrity: sha512-/BypzV0H1y1HzgYpxqRaXGBRqfodgoBBCcsrujT6QRcakDQdfU+Lq9PENPh5jB4I44YWq+0C2eHsHya+nZY1sA==} + /@rollup/rollup-win32-x64-msvc@4.17.2: + resolution: {integrity: sha512-TGGO7v7qOq4CYmSBVEYpI1Y5xDuCEnbVC5Vth8mOsW0gDSzxNrVERPc790IGHsrT2dQSimgMr9Ub3Y1Jci5/8w==} cpu: [x64] os: [win32] requiresBuild: true @@ -748,96 +592,48 @@ packages: resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} dev: true - /@tootallnate/once@2.0.0: - resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==} - engines: {node: '>= 10'} - dev: false - /@types/estree@1.0.5: resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} dev: true - /@types/negotiator@0.6.3: - resolution: {integrity: sha512-JkXTOdKs5MF086b/pt8C3+yVp3iDUwG635L7oCH6HvJvvr6lSUU5oe/gLXnPEfYRROHjJIPgCV6cuAg8gGkntQ==} - dev: true - - /@types/node@20.12.7: - resolution: {integrity: sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==} + /@types/node@20.11.0: + resolution: {integrity: sha512-o9bjXmDNcF7GbM4CNQpmi+TutCgap/K3w1JyKgxAjqx41zp9qlIAVFi0IhCNsJcXolEqLWhbFbEeL0PvYm4pcQ==} dependencies: undici-types: 5.26.5 dev: true - /@vitest/expect@1.4.0: - resolution: {integrity: sha512-Jths0sWCJZ8BxjKe+p+eKsoqev1/T8lYcrjavEaz8auEJ4jAVY0GwW3JKmdVU4mmNPLPHixh4GNXP7GFtAiDHA==} - dependencies: - '@vitest/spy': 1.4.0 - '@vitest/utils': 1.4.0 - chai: 4.4.1 - dev: true - - /@vitest/expect@1.5.0: - resolution: {integrity: sha512-0pzuCI6KYi2SIC3LQezmxujU9RK/vwC1U9R0rLuGlNGcOuDWxqWKu6nUdFsX9tH1WU0SXtAxToOsEjeUn1s3hA==} + /@vitest/expect@1.2.0: + resolution: {integrity: sha512-H+2bHzhyvgp32o7Pgj2h9RTHN0pgYaoi26Oo3mE+dCi1PAqV31kIIVfTbqMO3Bvshd5mIrJLc73EwSRrbol9Lw==} dependencies: - '@vitest/spy': 1.5.0 - '@vitest/utils': 1.5.0 + '@vitest/spy': 1.2.0 + '@vitest/utils': 1.2.0 chai: 4.4.1 dev: true - /@vitest/runner@1.4.0: - resolution: {integrity: sha512-EDYVSmesqlQ4RD2VvWo3hQgTJ7ZrFQ2VSJdfiJiArkCerDAGeyF1i6dHkmySqk573jLp6d/cfqCN+7wUB5tLgg==} - dependencies: - '@vitest/utils': 1.4.0 - p-limit: 5.0.0 - pathe: 1.1.2 - dev: true - - /@vitest/runner@1.5.0: - resolution: {integrity: sha512-7HWwdxXP5yDoe7DTpbif9l6ZmDwCzcSIK38kTSIt6CFEpMjX4EpCgT6wUmS0xTXqMI6E/ONmfgRKmaujpabjZQ==} + /@vitest/runner@1.2.0: + resolution: {integrity: sha512-vaJkDoQaNUTroT70OhM0NPznP7H3WyRwt4LvGwCVYs/llLaqhoSLnlIhUClZpbF5RgAee29KRcNz0FEhYcgxqA==} dependencies: - '@vitest/utils': 1.5.0 + '@vitest/utils': 1.2.0 p-limit: 5.0.0 pathe: 1.1.2 dev: true - /@vitest/snapshot@1.4.0: - resolution: {integrity: sha512-saAFnt5pPIA5qDGxOHxJ/XxhMFKkUSBJmVt5VgDsAqPTX6JP326r5C/c9UuCMPoXNzuudTPsYDZCoJ5ilpqG2A==} - dependencies: - magic-string: 0.30.9 - pathe: 1.1.2 - pretty-format: 29.7.0 - dev: true - - /@vitest/snapshot@1.5.0: - resolution: {integrity: sha512-qpv3fSEuNrhAO3FpH6YYRdaECnnRjg9VxbhdtPwPRnzSfHVXnNzzrpX4cJxqiwgRMo7uRMWDFBlsBq4Cr+rO3A==} + /@vitest/snapshot@1.2.0: + resolution: {integrity: sha512-P33EE7TrVgB3HDLllrjK/GG6WSnmUtWohbwcQqmm7TAk9AVHpdgf7M3F3qRHKm6vhr7x3eGIln7VH052Smo6Kw==} dependencies: - magic-string: 0.30.9 + magic-string: 0.30.10 pathe: 1.1.2 pretty-format: 29.7.0 dev: true - /@vitest/spy@1.4.0: - resolution: {integrity: sha512-Ywau/Qs1DzM/8Uc+yA77CwSegizMlcgTJuYGAi0jujOteJOUf1ujunHThYo243KG9nAyWT3L9ifPYZ5+As/+6Q==} - dependencies: - tinyspy: 2.2.1 - dev: true - - /@vitest/spy@1.5.0: - resolution: {integrity: sha512-vu6vi6ew5N5MMHJjD5PoakMRKYdmIrNJmyfkhRpQt5d9Ewhw9nZ5Aqynbi3N61bvk9UvZ5UysMT6ayIrZ8GA9w==} + /@vitest/spy@1.2.0: + resolution: {integrity: sha512-MNxSAfxUaCeowqyyGwC293yZgk7cECZU9wGb8N1pYQ0yOn/SIr8t0l9XnGRdQZvNV/ZHBYu6GO/W3tj5K3VN1Q==} dependencies: tinyspy: 2.2.1 dev: true - /@vitest/utils@1.4.0: - resolution: {integrity: sha512-mx3Yd1/6e2Vt/PUC98DcqTirtfxUyAZ32uK82r8rZzbtBeBo+nqgnjx/LvqQdWsrvNtm14VmurNgcf4nqY5gJg==} - dependencies: - diff-sequences: 29.6.3 - estree-walker: 3.0.3 - loupe: 2.3.7 - pretty-format: 29.7.0 - dev: true - - /@vitest/utils@1.5.0: - resolution: {integrity: sha512-BDU0GNL8MWkRkSRdNFvCUCAVOeHaUlVJ9Tx0TYBZyXaaOTmGtUFObzchCivIBrIwKzvZA7A9sCejVhXM2aY98A==} + /@vitest/utils@1.2.0: + resolution: {integrity: sha512-FyD5bpugsXlwVpTcGLDf3wSPYy8g541fQt14qtzo8mJ4LdEpDKZ9mQy2+qdJm2TZRpjY5JLXihXCgIxiRJgi5g==} dependencies: diff-sequences: 29.6.3 estree-walker: 3.0.3 @@ -845,10 +641,6 @@ packages: pretty-format: 29.7.0 dev: true - /abbrev@1.1.1: - resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} - dev: false - /acorn-walk@8.3.2: resolution: {integrity: sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==} engines: {node: '>=0.4.0'} @@ -860,33 +652,10 @@ packages: hasBin: true dev: true - /agent-base@6.0.2: - resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} - engines: {node: '>= 6.0.0'} - dependencies: - debug: 4.3.4 - transitivePeerDependencies: - - supports-color - dev: false - - /agentkeepalive@4.5.0: - resolution: {integrity: sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==} - engines: {node: '>= 8.0.0'} - dependencies: - humanize-ms: 1.2.1 - dev: false - - /aggregate-error@3.1.0: - resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} - engines: {node: '>=8'} - dependencies: - clean-stack: 2.2.0 - indent-string: 4.0.0 - dev: false - /ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} + dev: true /ansi-regex@6.0.1: resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} @@ -905,34 +674,10 @@ packages: engines: {node: '>=10'} dev: true - /aproba@2.0.0: - resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==} - dev: false - - /are-we-there-yet@2.0.0: - resolution: {integrity: sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==} - engines: {node: '>=10'} - dependencies: - delegates: 1.0.0 - readable-stream: 3.6.2 - dev: false - - /are-we-there-yet@3.0.1: - resolution: {integrity: sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - dependencies: - delegates: 1.0.0 - readable-stream: 3.6.2 - dev: false - /assertion-error@1.1.0: resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} dev: true - /balanced-match@1.0.2: - resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - dev: false - /base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} dev: true @@ -945,19 +690,6 @@ packages: readable-stream: 3.6.2 dev: true - /brace-expansion@1.1.11: - resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} - dependencies: - balanced-match: 1.0.2 - concat-map: 0.0.1 - dev: false - - /brace-expansion@2.0.1: - resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} - dependencies: - balanced-match: 1.0.2 - dev: false - /buffer@6.0.3: resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} dependencies: @@ -970,32 +702,6 @@ packages: engines: {node: '>=8'} dev: true - /cacache@16.1.3: - resolution: {integrity: sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - dependencies: - '@npmcli/fs': 2.1.2 - '@npmcli/move-file': 2.0.1 - chownr: 2.0.0 - fs-minipass: 2.1.0 - glob: 8.1.0 - infer-owner: 1.0.4 - lru-cache: 7.18.3 - minipass: 3.3.6 - minipass-collect: 1.0.2 - minipass-flush: 1.0.5 - minipass-pipeline: 1.2.4 - mkdirp: 1.0.4 - p-map: 4.0.0 - promise-inflight: 1.0.1 - rimraf: 3.0.2 - ssri: 9.0.1 - tar: 6.2.1 - unique-filename: 2.0.1 - transitivePeerDependencies: - - bluebird - dev: false - /camelcase@5.3.1: resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} engines: {node: '>=6'} @@ -1025,16 +731,6 @@ packages: get-func-name: 2.0.2 dev: true - /chownr@2.0.0: - resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} - engines: {node: '>=10'} - dev: false - - /clean-stack@2.2.0: - resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} - engines: {node: '>=6'} - dev: false - /cli-cursor@4.0.0: resolution: {integrity: sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -1075,14 +771,9 @@ packages: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} dev: true - /color-support@1.1.3: - resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==} - hasBin: true - dev: false - - /concat-map@0.0.1: - resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - dev: false + /confbox@0.1.7: + resolution: {integrity: sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==} + dev: true /configstore@5.0.1: resolution: {integrity: sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==} @@ -1096,10 +787,6 @@ packages: xdg-basedir: 4.0.0 dev: true - /console-control-strings@1.1.0: - resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==} - dev: false - /cross-spawn@7.0.3: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} @@ -1124,6 +811,7 @@ packages: optional: true dependencies: ms: 2.1.2 + dev: true /decamelize@1.2.0: resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} @@ -1143,20 +831,11 @@ packages: hasBin: true dev: true - /delegates@1.0.0: - resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} - dev: false - /detect-indent@6.1.0: resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} engines: {node: '>=8'} dev: true - /detect-libc@2.0.3: - resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==} - engines: {node: '>=8'} - dev: false - /diff-sequences@29.6.3: resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -1174,29 +853,6 @@ packages: engines: {node: '>=12'} dev: true - /duckdb-async@0.10.0: - resolution: {integrity: sha512-YMDYulx5JLzT01CkqfG813h3ikCF3Pf7nV2xH1OZdVB6q0A6Rofrtf0x1CIotXY33fifgFY1jte3WPzD+3708Q==} - dependencies: - duckdb: 0.10.0 - transitivePeerDependencies: - - bluebird - - encoding - - supports-color - dev: false - - /duckdb@0.10.0: - resolution: {integrity: sha512-+yVccZZTCYVHccKUUKeqCiQQ5/jggdnfyafN01Cgzzx2F0NSSSqNO9vT39oLmpIhEqsJIHYXVC/LuawN3AxUyA==} - requiresBuild: true - dependencies: - '@mapbox/node-pre-gyp': 1.0.11 - node-addon-api: 7.1.0 - node-gyp: 9.4.1 - transitivePeerDependencies: - - bluebird - - encoding - - supports-color - dev: false - /eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} dev: true @@ -1207,23 +863,7 @@ packages: /emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - - /encoding@0.1.13: - resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==} - requiresBuild: true - dependencies: - iconv-lite: 0.6.3 - dev: false - optional: true - - /env-paths@2.2.1: - resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} - engines: {node: '>=6'} - dev: false - - /err-code@2.0.3: - resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==} - dev: false + dev: true /esbuild@0.19.12: resolution: {integrity: sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==} @@ -1254,6 +894,7 @@ packages: '@esbuild/win32-arm64': 0.19.12 '@esbuild/win32-ia32': 0.19.12 '@esbuild/win32-x64': 0.19.12 + dev: true /esbuild@0.20.2: resolution: {integrity: sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==} @@ -1312,10 +953,6 @@ packages: strip-final-newline: 3.0.0 dev: true - /exponential-backoff@3.1.1: - resolution: {integrity: sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==} - dev: false - /find-up@4.1.0: resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} engines: {node: '>=8'} @@ -1324,57 +961,18 @@ packages: path-exists: 4.0.0 dev: true - /fs-minipass@2.1.0: - resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} - engines: {node: '>= 8'} - dependencies: - minipass: 3.3.6 - dev: false - - /fs.realpath@1.0.0: - resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} - dev: false - /fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] requiresBuild: true + dev: true optional: true /fuzzy-search@3.2.1: resolution: {integrity: sha512-vAcPiyomt1ioKAsAL2uxSABHJ4Ju/e4UeDM+g1OlR0vV4YhLGMNsdLNvZTpEDY4JCSt0E4hASCNM5t2ETtsbyg==} dev: true - /gauge@3.0.2: - resolution: {integrity: sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==} - engines: {node: '>=10'} - dependencies: - aproba: 2.0.0 - color-support: 1.1.3 - console-control-strings: 1.1.0 - has-unicode: 2.0.1 - object-assign: 4.1.1 - signal-exit: 3.0.7 - string-width: 4.2.3 - strip-ansi: 6.0.1 - wide-align: 1.1.5 - dev: false - - /gauge@4.0.4: - resolution: {integrity: sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - dependencies: - aproba: 2.0.0 - color-support: 1.1.3 - console-control-strings: 1.1.0 - has-unicode: 2.0.1 - signal-exit: 3.0.7 - string-width: 4.2.3 - strip-ansi: 6.0.1 - wide-align: 1.1.5 - dev: false - /get-caller-file@2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} @@ -1389,143 +987,47 @@ packages: engines: {node: '>=16'} dev: true - /get-tsconfig@4.7.3: - resolution: {integrity: sha512-ZvkrzoUA0PQZM6fy6+/Hce561s+faD1rsNwhnO5FelNjyy7EMGJ3Rz1AQ8GYDWjhRs/7dBLOEJvhK8MiEJOAFg==} - dependencies: - resolve-pkg-maps: 1.0.0 - dev: false - /git-config-path@2.0.0: resolution: {integrity: sha512-qc8h1KIQbJpp+241id3GuAtkdyJ+IK+LIVtkiFTRKRrmddDzs3SI9CvP1QYmWBFvm1I/PWRwj//of8bgAc0ltA==} engines: {node: '>=4'} dev: true - /glob@7.2.3: - resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 3.1.2 - once: 1.4.0 - path-is-absolute: 1.0.1 - dev: false - - /glob@8.1.0: - resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} - engines: {node: '>=12'} - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 5.1.6 - once: 1.4.0 - dev: false - /graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + dev: true - /has-unicode@2.0.1: - resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==} - dev: false + /human-signals@5.0.0: + resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} + engines: {node: '>=16.17.0'} + dev: true - /http-cache-semantics@4.1.1: - resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} - dev: false - - /http-proxy-agent@5.0.0: - resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==} - engines: {node: '>= 6'} - dependencies: - '@tootallnate/once': 2.0.0 - agent-base: 6.0.2 - debug: 4.3.4 - transitivePeerDependencies: - - supports-color - dev: false - - /https-proxy-agent@5.0.1: - resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} - engines: {node: '>= 6'} - dependencies: - agent-base: 6.0.2 - debug: 4.3.4 - transitivePeerDependencies: - - supports-color - dev: false - - /human-signals@5.0.0: - resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} - engines: {node: '>=16.17.0'} - dev: true - - /humanize-ms@1.2.1: - resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} - dependencies: - ms: 2.1.2 - dev: false - - /iconv-lite@0.6.3: - resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} - engines: {node: '>=0.10.0'} - requiresBuild: true - dependencies: - safer-buffer: 2.1.2 - dev: false - optional: true - - /ieee754@1.2.1: - resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - dev: true + /ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + dev: true /imurmurhash@0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} - - /indent-string@4.0.0: - resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} - engines: {node: '>=8'} - dev: false - - /infer-owner@1.0.4: - resolution: {integrity: sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==} - dev: false - - /inflight@1.0.6: - resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} - dependencies: - once: 1.4.0 - wrappy: 1.0.2 - dev: false + dev: true /inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + dev: true /ini@1.3.8: resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} dev: true - /ip-address@9.0.5: - resolution: {integrity: sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==} - engines: {node: '>= 12'} - dependencies: - jsbn: 1.1.0 - sprintf-js: 1.1.3 - dev: false - /is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} + dev: true /is-interactive@2.0.0: resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} engines: {node: '>=12'} dev: true - /is-lambda@1.0.1: - resolution: {integrity: sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==} - dev: false - /is-obj@2.0.0: resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} engines: {node: '>=8'} @@ -1547,17 +1049,6 @@ packages: /isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - - /js-tokens@9.0.0: - resolution: {integrity: sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ==} - dev: true - - /jsbn@1.1.0: - resolution: {integrity: sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==} - dev: false - - /jsonc-parser@3.2.1: - resolution: {integrity: sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==} dev: true /kleur@3.0.3: @@ -1584,8 +1075,8 @@ packages: resolution: {integrity: sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==} engines: {node: '>=14'} dependencies: - mlly: 1.6.1 - pkg-types: 1.0.3 + mlly: 1.7.0 + pkg-types: 1.1.1 dev: true /locate-path@5.0.0: @@ -1609,21 +1100,8 @@ packages: get-func-name: 2.0.2 dev: true - /lru-cache@6.0.0: - resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} - engines: {node: '>=10'} - dependencies: - yallist: 4.0.0 - dev: false - - /lru-cache@7.18.3: - resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} - engines: {node: '>=12'} - dev: false - - /magic-string@0.30.9: - resolution: {integrity: sha512-S1+hd+dIrC8EZqKyT9DstTH/0Z+f76kmmvZnkfQVmOpDEF9iVgdYif3Q/pIWHmCoo59bQVGW0kVL3e2nl+9+Sw==} - engines: {node: '>=12'} + /magic-string@0.30.10: + resolution: {integrity: sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==} dependencies: '@jridgewell/sourcemap-codec': 1.4.15 dev: true @@ -1633,31 +1111,7 @@ packages: engines: {node: '>=8'} dependencies: semver: 6.3.1 - - /make-fetch-happen@10.2.1: - resolution: {integrity: sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - dependencies: - agentkeepalive: 4.5.0 - cacache: 16.1.3 - http-cache-semantics: 4.1.1 - http-proxy-agent: 5.0.0 - https-proxy-agent: 5.0.1 - is-lambda: 1.0.1 - lru-cache: 7.18.3 - minipass: 3.3.6 - minipass-collect: 1.0.2 - minipass-fetch: 2.1.2 - minipass-flush: 1.0.5 - minipass-pipeline: 1.2.4 - negotiator: 0.6.3 - promise-retry: 2.0.1 - socks-proxy-agent: 7.0.0 - ssri: 9.0.1 - transitivePeerDependencies: - - bluebird - - supports-color - dev: false + dev: true /merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} @@ -1673,95 +1127,18 @@ packages: engines: {node: '>=12'} dev: true - /minimatch@3.1.2: - resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} - dependencies: - brace-expansion: 1.1.11 - dev: false - - /minimatch@5.1.6: - resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} - engines: {node: '>=10'} - dependencies: - brace-expansion: 2.0.1 - dev: false - - /minipass-collect@1.0.2: - resolution: {integrity: sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==} - engines: {node: '>= 8'} - dependencies: - minipass: 3.3.6 - dev: false - - /minipass-fetch@2.1.2: - resolution: {integrity: sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - dependencies: - minipass: 3.3.6 - minipass-sized: 1.0.3 - minizlib: 2.1.2 - optionalDependencies: - encoding: 0.1.13 - dev: false - - /minipass-flush@1.0.5: - resolution: {integrity: sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==} - engines: {node: '>= 8'} - dependencies: - minipass: 3.3.6 - dev: false - - /minipass-pipeline@1.2.4: - resolution: {integrity: sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==} - engines: {node: '>=8'} - dependencies: - minipass: 3.3.6 - dev: false - - /minipass-sized@1.0.3: - resolution: {integrity: sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==} - engines: {node: '>=8'} - dependencies: - minipass: 3.3.6 - dev: false - - /minipass@3.3.6: - resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} - engines: {node: '>=8'} - dependencies: - yallist: 4.0.0 - dev: false - - /minipass@5.0.0: - resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} - engines: {node: '>=8'} - dev: false - - /minizlib@2.1.2: - resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} - engines: {node: '>= 8'} - dependencies: - minipass: 3.3.6 - yallist: 4.0.0 - dev: false - - /mkdirp@1.0.4: - resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} - engines: {node: '>=10'} - hasBin: true - dev: false - - /mlly@1.6.1: - resolution: {integrity: sha512-vLgaHvaeunuOXHSmEbZ9izxPx3USsk8KCQ8iC+aTlp5sKRSoZvwhHh5L9VbKSaVC6sJDqbyohIS76E2VmHIPAA==} + /mlly@1.7.0: + resolution: {integrity: sha512-U9SDaXGEREBYQgfejV97coK0UL1r+qnF2SyO9A3qcI8MzKnsIFKHNVEkrDyNncQTKQQumsasmeq84eNMdBfsNQ==} dependencies: acorn: 8.11.3 pathe: 1.1.2 - pkg-types: 1.0.3 + pkg-types: 1.1.1 ufo: 1.5.3 dev: true /ms@2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + dev: true /nanoid@3.3.7: resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} @@ -1769,65 +1146,6 @@ packages: hasBin: true dev: true - /negotiator@0.6.3: - resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} - engines: {node: '>= 0.6'} - dev: false - - /node-addon-api@7.1.0: - resolution: {integrity: sha512-mNcltoe1R8o7STTegSOHdnJNN7s5EUvhoS7ShnTHDyOSd+8H+UdWODq6qSv67PjC8Zc5JRT8+oLAMCr0SIXw7g==} - engines: {node: ^16 || ^18 || >= 20} - dev: false - - /node-fetch@2.7.0: - resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} - engines: {node: 4.x || >=6.0.0} - peerDependencies: - encoding: ^0.1.0 - peerDependenciesMeta: - encoding: - optional: true - dependencies: - whatwg-url: 5.0.0 - dev: false - - /node-gyp@9.4.1: - resolution: {integrity: sha512-OQkWKbjQKbGkMf/xqI1jjy3oCTgMKJac58G2+bjZb3fza6gW2YrCSdMQYaoTb70crvE//Gngr4f0AgVHmqHvBQ==} - engines: {node: ^12.13 || ^14.13 || >=16} - hasBin: true - dependencies: - env-paths: 2.2.1 - exponential-backoff: 3.1.1 - glob: 7.2.3 - graceful-fs: 4.2.11 - make-fetch-happen: 10.2.1 - nopt: 6.0.0 - npmlog: 6.0.2 - rimraf: 3.0.2 - semver: 7.6.0 - tar: 6.2.1 - which: 2.0.2 - transitivePeerDependencies: - - bluebird - - supports-color - dev: false - - /nopt@5.0.0: - resolution: {integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==} - engines: {node: '>=6'} - hasBin: true - dependencies: - abbrev: 1.1.1 - dev: false - - /nopt@6.0.0: - resolution: {integrity: sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - hasBin: true - dependencies: - abbrev: 1.1.1 - dev: false - /npm-run-path@5.3.0: resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -1835,36 +1153,6 @@ packages: path-key: 4.0.0 dev: true - /npmlog@5.0.1: - resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==} - dependencies: - are-we-there-yet: 2.0.0 - console-control-strings: 1.1.0 - gauge: 3.0.2 - set-blocking: 2.0.0 - dev: false - - /npmlog@6.0.2: - resolution: {integrity: sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - dependencies: - are-we-there-yet: 3.0.1 - console-control-strings: 1.1.0 - gauge: 4.0.4 - set-blocking: 2.0.0 - dev: false - - /object-assign@4.1.1: - resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} - engines: {node: '>=0.10.0'} - dev: false - - /once@1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - dependencies: - wrappy: 1.0.2 - dev: false - /onetime@5.1.2: resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} engines: {node: '>=6'} @@ -1915,13 +1203,6 @@ packages: p-limit: 2.3.0 dev: true - /p-map@4.0.0: - resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==} - engines: {node: '>=10'} - dependencies: - aggregate-error: 3.1.0 - dev: false - /p-try@2.2.0: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} @@ -1944,11 +1225,6 @@ packages: engines: {node: '>=8'} dev: true - /path-is-absolute@1.0.1: - resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} - engines: {node: '>=0.10.0'} - dev: false - /path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} @@ -1967,15 +1243,15 @@ packages: resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} dev: true - /picocolors@1.0.0: - resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + /picocolors@1.0.1: + resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} dev: true - /pkg-types@1.0.3: - resolution: {integrity: sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==} + /pkg-types@1.1.1: + resolution: {integrity: sha512-ko14TjmDuQJ14zsotODv7dBlwxKhUKQEhuhmbqo1uCi9BB0Z2alo/wAXg6q1dTR5TyuqYyWhjtfe/Tsh+X28jQ==} dependencies: - jsonc-parser: 3.2.1 - mlly: 1.6.1 + confbox: 0.1.7 + mlly: 1.7.0 pathe: 1.1.2 dev: true @@ -1984,7 +1260,7 @@ packages: engines: {node: ^10 || ^12 || >=14} dependencies: nanoid: 3.3.7 - picocolors: 1.0.0 + picocolors: 1.0.1 source-map-js: 1.2.0 dev: true @@ -1999,10 +1275,10 @@ packages: dependencies: '@jest/schemas': 29.6.3 ansi-styles: 5.2.0 - react-is: 18.2.0 + react-is: 18.3.1 dev: true - /pridepack@2.6.0(tslib@2.6.2)(typescript@5.4.5): + /pridepack@2.6.0(tslib@2.6.2)(typescript@5.3.3): resolution: {integrity: sha512-K81TouT+M3zwzPvDi70/CFVtzADvGpn071zAMm419ULb29gZni21pJ24njDFm3O+lJn0txBl4x1dsFBLWqS4iQ==} engines: {node: '>=16'} hasBin: true @@ -2021,27 +1297,10 @@ packages: pretty-bytes: 6.1.1 prompts: 2.4.2 tslib: 2.6.2 - typescript: 5.4.5 + typescript: 5.3.3 yargs: 17.7.2 dev: true - /promise-inflight@1.0.1: - resolution: {integrity: sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==} - peerDependencies: - bluebird: '*' - peerDependenciesMeta: - bluebird: - optional: true - dev: false - - /promise-retry@2.0.1: - resolution: {integrity: sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==} - engines: {node: '>=10'} - dependencies: - err-code: 2.0.3 - retry: 0.12.0 - dev: false - /prompts@2.4.2: resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} engines: {node: '>= 6'} @@ -2050,8 +1309,8 @@ packages: sisteransi: 1.0.5 dev: true - /react-is@18.2.0: - resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} + /react-is@18.3.1: + resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} dev: true /readable-stream@3.6.2: @@ -2061,6 +1320,7 @@ packages: inherits: 2.0.4 string_decoder: 1.3.0 util-deprecate: 1.0.2 + dev: true /require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} @@ -2071,10 +1331,6 @@ packages: resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} dev: true - /resolve-pkg-maps@1.0.0: - resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} - dev: false - /restore-cursor@4.0.0: resolution: {integrity: sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -2083,67 +1339,44 @@ packages: signal-exit: 3.0.7 dev: true - /retry@0.12.0: - resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} - engines: {node: '>= 4'} - dev: false - - /rimraf@3.0.2: - resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} - hasBin: true - dependencies: - glob: 7.2.3 - dev: false - - /rollup@4.14.3: - resolution: {integrity: sha512-ag5tTQKYsj1bhrFC9+OEWqb5O6VYgtQDO9hPDBMmIbePwhfSr+ExlcU741t8Dhw5DkPCQf6noz0jb36D6W9/hw==} + /rollup@4.17.2: + resolution: {integrity: sha512-/9ClTJPByC0U4zNLowV1tMBe8yMEAxewtR3cUNX5BoEpGH3dQEWpJLr6CLp0fPdYRF/fzVOgvDb1zXuakwF5kQ==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true dependencies: '@types/estree': 1.0.5 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.14.3 - '@rollup/rollup-android-arm64': 4.14.3 - '@rollup/rollup-darwin-arm64': 4.14.3 - '@rollup/rollup-darwin-x64': 4.14.3 - '@rollup/rollup-linux-arm-gnueabihf': 4.14.3 - '@rollup/rollup-linux-arm-musleabihf': 4.14.3 - '@rollup/rollup-linux-arm64-gnu': 4.14.3 - '@rollup/rollup-linux-arm64-musl': 4.14.3 - '@rollup/rollup-linux-powerpc64le-gnu': 4.14.3 - '@rollup/rollup-linux-riscv64-gnu': 4.14.3 - '@rollup/rollup-linux-s390x-gnu': 4.14.3 - '@rollup/rollup-linux-x64-gnu': 4.14.3 - '@rollup/rollup-linux-x64-musl': 4.14.3 - '@rollup/rollup-win32-arm64-msvc': 4.14.3 - '@rollup/rollup-win32-ia32-msvc': 4.14.3 - '@rollup/rollup-win32-x64-msvc': 4.14.3 + '@rollup/rollup-android-arm-eabi': 4.17.2 + '@rollup/rollup-android-arm64': 4.17.2 + '@rollup/rollup-darwin-arm64': 4.17.2 + '@rollup/rollup-darwin-x64': 4.17.2 + '@rollup/rollup-linux-arm-gnueabihf': 4.17.2 + '@rollup/rollup-linux-arm-musleabihf': 4.17.2 + '@rollup/rollup-linux-arm64-gnu': 4.17.2 + '@rollup/rollup-linux-arm64-musl': 4.17.2 + '@rollup/rollup-linux-powerpc64le-gnu': 4.17.2 + '@rollup/rollup-linux-riscv64-gnu': 4.17.2 + '@rollup/rollup-linux-s390x-gnu': 4.17.2 + '@rollup/rollup-linux-x64-gnu': 4.17.2 + '@rollup/rollup-linux-x64-musl': 4.17.2 + '@rollup/rollup-win32-arm64-msvc': 4.17.2 + '@rollup/rollup-win32-ia32-msvc': 4.17.2 + '@rollup/rollup-win32-x64-msvc': 4.17.2 fsevents: 2.3.3 dev: true /safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - - /safer-buffer@2.1.2: - resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - requiresBuild: true - dev: false - optional: true + dev: true /semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true - - /semver@7.6.0: - resolution: {integrity: sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==} - engines: {node: '>=10'} - hasBin: true - dependencies: - lru-cache: 6.0.0 - dev: false + dev: true /set-blocking@2.0.0: resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} + dev: true /shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} @@ -2163,6 +1396,7 @@ packages: /signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + dev: true /signal-exit@4.1.0: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} @@ -2173,46 +1407,11 @@ packages: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} dev: true - /smart-buffer@4.2.0: - resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} - engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} - dev: false - - /socks-proxy-agent@7.0.0: - resolution: {integrity: sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==} - engines: {node: '>= 10'} - dependencies: - agent-base: 6.0.2 - debug: 4.3.4 - socks: 2.8.3 - transitivePeerDependencies: - - supports-color - dev: false - - /socks@2.8.3: - resolution: {integrity: sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==} - engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} - dependencies: - ip-address: 9.0.5 - smart-buffer: 4.2.0 - dev: false - /source-map-js@1.2.0: resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} engines: {node: '>=0.10.0'} dev: true - /sprintf-js@1.1.3: - resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==} - dev: false - - /ssri@9.0.1: - resolution: {integrity: sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - dependencies: - minipass: 3.3.6 - dev: false - /stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} dev: true @@ -2235,6 +1434,7 @@ packages: emoji-regex: 8.0.0 is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 + dev: true /string-width@6.1.0: resolution: {integrity: sha512-k01swCJAgQmuADB0YIc+7TuatfNvTBVOoaUWJjTB9R4VJzR5vNWzf5t42ESVZFPS8xTySF7CAdV4t/aaIm3UnQ==} @@ -2249,12 +1449,14 @@ packages: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} dependencies: safe-buffer: 5.2.1 + dev: true /strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} dependencies: ansi-regex: 5.0.1 + dev: true /strip-ansi@7.1.0: resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} @@ -2268,26 +1470,14 @@ packages: engines: {node: '>=12'} dev: true - /strip-literal@2.1.0: - resolution: {integrity: sha512-Op+UycaUt/8FbN/Z2TWPBLge3jWrP3xj10f3fnYxf052bKuS3EKs1ZQcVGjnEMdsNVAM+plXRdmjrZ/KgG3Skw==} + /strip-literal@1.3.0: + resolution: {integrity: sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==} dependencies: - js-tokens: 9.0.0 + acorn: 8.11.3 dev: true - /tar@6.2.1: - resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} - engines: {node: '>=10'} - dependencies: - chownr: 2.0.0 - fs-minipass: 2.1.0 - minipass: 5.0.0 - minizlib: 2.1.2 - mkdirp: 1.0.4 - yallist: 4.0.0 - dev: false - - /tinybench@2.7.0: - resolution: {integrity: sha512-Qgayeb106x2o4hNzNjsZEfFziw8IbKqtbXBjVh7VIZfBxfD5M4gWtpyx5+YTae2gJ6Y6Dz/KLepiv16RFeQWNA==} + /tinybench@2.8.0: + resolution: {integrity: sha512-1/eK7zUnIklz4JUUlL+658n58XO2hHLQfSk1Zf2LKieUjxidN16eKFEoDEfjHc3ohofSSqK3X5yO6VGb6iW8Lw==} dev: true /tinypool@0.8.4: @@ -2300,25 +1490,10 @@ packages: engines: {node: '>=14.0.0'} dev: true - /tr46@0.0.3: - resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} - dev: false - /tslib@2.6.2: resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} dev: true - /tsx@4.7.2: - resolution: {integrity: sha512-BCNd4kz6fz12fyrgCTEdZHGJ9fWTGeUzXmQysh0RVocDY3h4frk05ZNCXSy4kIenF7y/QnrdiVpTsyNRn6vlAw==} - engines: {node: '>=18.0.0'} - hasBin: true - dependencies: - esbuild: 0.19.12 - get-tsconfig: 4.7.3 - optionalDependencies: - fsevents: 2.3.3 - dev: false - /type-detect@4.0.8: resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} engines: {node: '>=4'} @@ -2330,8 +1505,8 @@ packages: is-typedarray: 1.0.0 dev: true - /typescript@5.4.5: - resolution: {integrity: sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==} + /typescript@5.3.3: + resolution: {integrity: sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==} engines: {node: '>=14.17'} hasBin: true dev: true @@ -2344,20 +1519,6 @@ packages: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} dev: true - /unique-filename@2.0.1: - resolution: {integrity: sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - dependencies: - unique-slug: 3.0.0 - dev: false - - /unique-slug@3.0.0: - resolution: {integrity: sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - dependencies: - imurmurhash: 0.1.4 - dev: false - /unique-string@2.0.0: resolution: {integrity: sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==} engines: {node: '>=8'} @@ -2367,42 +1528,22 @@ packages: /util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + dev: true /valibot@0.30.0: resolution: {integrity: sha512-5POBdbSkM+3nvJ6ZlyQHsggisfRtyT4tVTo1EIIShs6qCdXJnyWU5TJ68vr8iTg5zpOLjXLRiBqNx+9zwZz/rA==} dev: false - /vite-node@1.4.0(@types/node@20.12.7): - resolution: {integrity: sha512-VZDAseqjrHgNd4Kh8icYHWzTKSCZMhia7GyHfhtzLW33fZlG9SwsB6CEhgyVOWkJfJ2pFLrp/Gj1FSfAiqH9Lw==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true - dependencies: - cac: 6.7.14 - debug: 4.3.4 - pathe: 1.1.2 - picocolors: 1.0.0 - vite: 5.2.8(@types/node@20.12.7) - transitivePeerDependencies: - - '@types/node' - - less - - lightningcss - - sass - - stylus - - sugarss - - supports-color - - terser - dev: true - - /vite-node@1.5.0(@types/node@20.12.7): - resolution: {integrity: sha512-tV8h6gMj6vPzVCa7l+VGq9lwoJjW8Y79vst8QZZGiuRAfijU+EEWuc0kFpmndQrWhMMhet1jdSF+40KSZUqIIw==} + /vite-node@1.2.0(@types/node@20.11.0): + resolution: {integrity: sha512-ETnQTHeAbbOxl7/pyBck9oAPZZZo+kYnFt1uQDD+hPReOc+wCjXw4r4jHriBRuVDB5isHmPXxrfc1yJnfBERqg==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true dependencies: cac: 6.7.14 debug: 4.3.4 pathe: 1.1.2 - picocolors: 1.0.0 - vite: 5.2.8(@types/node@20.12.7) + picocolors: 1.0.1 + vite: 5.2.11(@types/node@20.11.0) transitivePeerDependencies: - '@types/node' - less @@ -2414,8 +1555,8 @@ packages: - terser dev: true - /vite@5.2.8(@types/node@20.12.7): - resolution: {integrity: sha512-OyZR+c1CE8yeHw5V5t59aXsUPPVTHMDjEZz8MgguLL/Q7NblxhZUlTu9xSPqlsUO/y+X7dlU05jdhvyycD55DA==} + /vite@5.2.11(@types/node@20.11.0): + resolution: {integrity: sha512-HndV31LWW05i1BLPMUCE1B9E9GFbOu1MbenhS58FuK6owSO5qHm7GiCotrNY1YE5rMeQSFBGmT5ZaLEjFizgiQ==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: @@ -2442,23 +1583,23 @@ packages: terser: optional: true dependencies: - '@types/node': 20.12.7 + '@types/node': 20.11.0 esbuild: 0.20.2 postcss: 8.4.38 - rollup: 4.14.3 + rollup: 4.17.2 optionalDependencies: fsevents: 2.3.3 dev: true - /vitest@1.4.0(@types/node@20.12.7): - resolution: {integrity: sha512-gujzn0g7fmwf83/WzrDTnncZt2UiXP41mHuFYFrdwaLRVQ6JYQEiME2IfEjU3vcFL3VKa75XhI3lFgn+hfVsQw==} + /vitest@1.2.0(@types/node@20.11.0): + resolution: {integrity: sha512-Ixs5m7BjqvLHXcibkzKRQUvD/XLw0E3rvqaCMlrm/0LMsA0309ZqYvTlPzkhh81VlEyVZXFlwWnkhb6/UMtcaQ==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@types/node': ^18.0.0 || >=20.0.0 - '@vitest/browser': 1.4.0 - '@vitest/ui': 1.4.0 + '@vitest/browser': ^1.0.0 + '@vitest/ui': ^1.0.0 happy-dom: '*' jsdom: '*' peerDependenciesMeta: @@ -2475,82 +1616,27 @@ packages: jsdom: optional: true dependencies: - '@types/node': 20.12.7 - '@vitest/expect': 1.4.0 - '@vitest/runner': 1.4.0 - '@vitest/snapshot': 1.4.0 - '@vitest/spy': 1.4.0 - '@vitest/utils': 1.4.0 - acorn-walk: 8.3.2 - chai: 4.4.1 - debug: 4.3.4 - execa: 8.0.1 - local-pkg: 0.5.0 - magic-string: 0.30.9 - pathe: 1.1.2 - picocolors: 1.0.0 - std-env: 3.7.0 - strip-literal: 2.1.0 - tinybench: 2.7.0 - tinypool: 0.8.4 - vite: 5.2.8(@types/node@20.12.7) - vite-node: 1.4.0(@types/node@20.12.7) - why-is-node-running: 2.2.2 - transitivePeerDependencies: - - less - - lightningcss - - sass - - stylus - - sugarss - - supports-color - - terser - dev: true - - /vitest@1.5.0(@types/node@20.12.7): - resolution: {integrity: sha512-d8UKgR0m2kjdxDWX6911uwxout6GHS0XaGH1cksSIVVG8kRlE7G7aBw7myKQCvDI5dT4j7ZMa+l706BIORMDLw==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true - peerDependencies: - '@edge-runtime/vm': '*' - '@types/node': ^18.0.0 || >=20.0.0 - '@vitest/browser': 1.5.0 - '@vitest/ui': 1.5.0 - 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 - dependencies: - '@types/node': 20.12.7 - '@vitest/expect': 1.5.0 - '@vitest/runner': 1.5.0 - '@vitest/snapshot': 1.5.0 - '@vitest/spy': 1.5.0 - '@vitest/utils': 1.5.0 + '@types/node': 20.11.0 + '@vitest/expect': 1.2.0 + '@vitest/runner': 1.2.0 + '@vitest/snapshot': 1.2.0 + '@vitest/spy': 1.2.0 + '@vitest/utils': 1.2.0 acorn-walk: 8.3.2 + cac: 6.7.14 chai: 4.4.1 debug: 4.3.4 execa: 8.0.1 local-pkg: 0.5.0 - magic-string: 0.30.9 + magic-string: 0.30.10 pathe: 1.1.2 - picocolors: 1.0.0 + picocolors: 1.0.1 std-env: 3.7.0 - strip-literal: 2.1.0 - tinybench: 2.7.0 + strip-literal: 1.3.0 + tinybench: 2.8.0 tinypool: 0.8.4 - vite: 5.2.8(@types/node@20.12.7) - vite-node: 1.5.0(@types/node@20.12.7) + vite: 5.2.11(@types/node@20.11.0) + vite-node: 1.2.0(@types/node@20.11.0) why-is-node-running: 2.2.2 transitivePeerDependencies: - less @@ -2562,17 +1648,6 @@ packages: - terser dev: true - /webidl-conversions@3.0.1: - resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} - dev: false - - /whatwg-url@5.0.0: - resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} - dependencies: - tr46: 0.0.3 - webidl-conversions: 3.0.1 - dev: false - /which-module@2.0.1: resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} dev: true @@ -2583,6 +1658,7 @@ packages: hasBin: true dependencies: isexe: 2.0.0 + dev: true /why-is-node-running@2.2.2: resolution: {integrity: sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==} @@ -2593,12 +1669,6 @@ packages: stackback: 0.0.2 dev: true - /wide-align@1.1.5: - resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} - dependencies: - string-width: 4.2.3 - dev: false - /wrap-ansi@6.2.0: resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} engines: {node: '>=8'} @@ -2621,10 +1691,6 @@ packages: resolution: {integrity: sha512-NWfjspSgMDXQIMpKM56AwCQPI01OMFRYYJBh6dGNCfH7AOl+j/VqqbiopgJ4VuQfSluqLc+2ekqaPNpYAGZ/Vg==} dev: true - /wrappy@1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - dev: false - /write-file-atomic@3.0.3: resolution: {integrity: sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==} dependencies: @@ -2648,10 +1714,6 @@ packages: engines: {node: '>=10'} dev: true - /yallist@4.0.0: - resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} - dev: false - /yargs-parser@18.1.3: resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} engines: {node: '>=6'}