diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index af60b8b..5146132 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -1,4 +1,4 @@ -### Development +# Development To work on the SDK generator you need to do the following: @@ -12,40 +12,135 @@ In production the root folder will be used. As of v1.9.0 this repository uses [conventional commit messages](https://conventionalcommits.org). -### Testing +# Testing -The project uses [Vitest](https://vitest.dev/) as its test framework. Tests are colocated with their source files in the `src/` directory. Each test file is named `.spec.ts` and placed next to the module it tests. +The project uses [Vitest](https://vitest.dev/) as its test framework and tests are divided into generator tests and SDK tests. Generator tests are run on the generator itself, while SDK tests are run on the generated SDK. -#### Running tests +## Generator tests + +Generator tests are colocated with their source files in the `src/` directory. Each test file is named `.spec.ts` and placed next to the module it tests. + +### Writing generator tests + +Generator test files should be placed next to the source file they test, within the `src/` directory. Each test file should be named `.spec.ts`. The path aliases from `tsconfig.node.json` (e.g. `@ts/`, `@utils/`) are available in test files via `vitest.config.ts`. + +Example (`src/utils/myModule.spec.ts`): + +```ts +import { describe, expect, it } from 'vitest'; +import { myFunction } from '@utils/myModule'; + +describe('myFunction', () => { + it('should return the expected result', () => { + expect(myFunction('input')).toBe('expected output'); + }); +}); +``` + +### Running generator tests ```sh # Run all tests once -$ npm run test +$ npm run test:generator # Run tests with coverage report $ npm run test:coverage ``` -#### Writing tests +## SDK tests -Test files should be placed next to the source file they test, within the `src/` directory. Each test file should be named `.spec.ts`. +SDK tests are run against the generated SDK itself. They live in the `test/` directory and are organized by feature and API version. -The path aliases from `tsconfig.node.json` (e.g. `@ts/`, `@utils/`) are available in test files via `vitest.config.ts`. +``` +test/ + / # e.g. unparameterized, use-query-language, deprecated, target/browser-rx + / # e.g. v3 + *.spec.ts +``` -Example (`src/utils/myModule.spec.ts`): +Each feature directory corresponds to a SDK generation flag (e.g. `--use-query-language`, `--deprecated`, `--generate-unique`, `--target`). Not all features have tests yet, but new test files will be added over time. + +### Writing SDK tests + +SDK test files are placed under `test///`, named `.spec.ts` and import directly from the generated SDK via the `@sdk/dist` path alias. + +Example (`test/unparameterized/v3/my-feature.spec.ts`): ```ts -import { describe, expect, it } from 'vitest'; -import { myFunction } from '@utils/myModule'; +import { afterAll, beforeAll, describe, expect, it } from 'vitest'; +import { articleService, setGlobalConfig } from '@sdk/dist'; + +describe('my feature', () => { + let capturedRequest: Request; + + beforeAll(() => { + setGlobalConfig({ + host: 'test.example.com', + secure: true, + key: 'test-key', + interceptors: { + request: (request) => { + capturedRequest = request; + return new Response(JSON.stringify({ result: [], referencedEntities: {}, additionalProperties: {} }), { + status: 200, + headers: { 'content-type': 'application/json' } + }); + } + } + }); + }); -describe('myFunction', () => { - it('should return the expected result', () => { - expect(myFunction('input')).toBe('expected output'); + it('should do something', async () => { + await articleService().some({}); + const url = new URL(capturedRequest.url); + expect(url.searchParams.get('someParam')).toBe('expectedValue'); + }); + + afterAll(() => { + setGlobalConfig(undefined); }); }); ``` -### Publishing +### Type checking + +Type checking for SDK tests is performed by `tsc` as part of the test run. Because each test run targets a specific set of feature/version folders, the TypeScript config used for type-checking is generated dynamically on the fly by `scripts/run-test-configuration.js` and written to `tsconfig.typecheck-generated.json` before `tsc` and `vitest` are invoked. + +The generated file looks like this: + +```json +{ + "extends": "./tsconfig.node.json", + "include": ["test///**/*.spec.ts"] +} +``` + +`tsconfig.typecheck-generated.json` is checked into the repository as a stub (with an empty `include` array) so that `tsconfig.json` can reference it permanently. This is required for `npm run build` to succeed and for `vite-tsconfig-paths` to correctly resolve the `@sdk/*` path alias at test runtime. The stub is overwritten each time the test script runs and should not be edited manually. + +### Running SDK tests + +SDK tests are run via `scripts/run-test-configuration.js`, which generates the SDK for the given configuration and then type-checks and runs only the matching test folders. + +```sh +# Run the default configuration (v3, browser target, no extra flags) +$ node scripts/run-test-configuration.js --version v3 + +# Run with a specific target +$ node scripts/run-test-configuration.js --version v3 --target node + +# Run with optional feature flags +$ node scripts/run-test-configuration.js --version v3 --use-query-language +$ node scripts/run-test-configuration.js --version v3 --deprecated +$ node scripts/run-test-configuration.js --version v3 --generate-unique + +# Flags can be combined +$ node scripts/run-test-configuration.js --version v3 --target browser.rx --use-query-language + +# Run the default CI configuration +$ npm run ci:test +``` + +# Publishing 1. Switch to master branch 2. To make a new version use script `npm run release` in local terminal diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d8d79f0..39bb6ec 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -34,9 +34,13 @@ jobs: name: build path: dist - test: - name: Test + generate: + name: Generate SDK runs-on: ubuntu-latest + needs: build + strategy: + matrix: + target: [node, node.rx, browser, browser.rx] steps: - name: Checkout repository uses: actions/checkout@v4 @@ -49,16 +53,56 @@ jobs: - name: Install dependencies run: npm ci - - name: Run tests - run: npm run test + - name: Download dist files + uses: actions/download-artifact@v4 + with: + name: build + path: dist - generate: - name: Generate SDK + - name: Generate SDK + run: | + chmod +x ./bin/cli.js + ./bin/cli.js test/openapi.json --target ${{ matrix.target }} + ./bin/cli.js test/openapi_v2.json --target ${{ matrix.target }} + ./bin/cli.js test/openapi_v3.json --target ${{ matrix.target }} + + test_generator: + name: Generator test + runs-on: ubuntu-latest + needs: build + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 24.11.1 + + - name: Install dependencies + run: npm ci + + - name: Download dist files + uses: actions/download-artifact@v4 + with: + name: build + path: dist + + - name: Run generator tests + run: npm run test:generator + + test: + name: Test runs-on: ubuntu-latest needs: build strategy: matrix: - target: [node, node.rx, browser, browser.rx] + version: [v3] + combo: + - '' + - '--use-query-language' + steps: - name: Checkout repository uses: actions/checkout@v4 @@ -77,11 +121,11 @@ jobs: name: build path: dist - - name: Generate SDK - run: | - chmod +x ./bin/cli.js - ./bin/cli.js test/openapi.json --target ${{ matrix.target }} - ./bin/cli.js test/openapi_v2.json --target ${{ matrix.target }} + - name: Make Generate SDK executable + run: chmod +x ./bin/cli.js + + - name: Run tests + run: node ./scripts/run-test-configuration.js --version ${{ matrix.version }} ${{ matrix.combo }} publish: name: Publish release version diff --git a/README.md b/README.md index 3dfa406..1d1bc25 100644 --- a/README.md +++ b/README.md @@ -349,7 +349,7 @@ wServices['article'].some({ where: { AND: [ { - OR: [{ name: { LIKE: '%test%', lower: true } }, { articleNumber: { LIKE: '%345%' } }] + OR: [{ name: { LIKE: '%test%', LOWER: true } }, { articleNumber: { LIKE: '%345%' } }] }, { batchNumberRequired: { EQ: true } } ] @@ -357,6 +357,14 @@ wServices['article'].some({ }); ``` +Some API operations like the arithmetic ones are not supported as type, due to their complexity. In this case a raw filter expression can be passed as string to `where` + +```ts +wServices['article'].some({ + where: '(articleLength * articleWidth * articleHeight) <= 3000' +}); +``` + "where" parameters are ANDed with other filter parameters. It is also possible to set an empty list within an IN-query: diff --git a/eslint.config.js b/eslint.config.js index 1788f5f..ce2dc6a 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -3,7 +3,7 @@ import tsEslint from 'typescript-eslint'; export default tsEslint.config( { name: 'app/files-to-ignore', - ignores: ['**/*.d.ts', '**/.*', '**/dist', '**/sdk', '**/node_modules'] + ignores: ['**/*.d.ts', '**/.*', '**/dist', '**/sdk', '**/node_modules', 'test/**'] }, { extends: [...tsEslint.configs.recommendedTypeChecked], diff --git a/package-lock.json b/package-lock.json index f258582..7c77301 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,7 @@ "indent-string": "5.0.0", "openapi-types": "12.1.3", "pretty-ms": "8.0.0", - "rolldown": "^1.0.0-rc.10", + "rolldown": "1.0.0-rc.15", "typescript": "5.9.3", "yargs": "17.7.2" }, @@ -31,7 +31,7 @@ "eslint": "10.0.2", "prettier": "3.3.3", "typescript-eslint": "8.56.1", - "vite-tsconfig-paths": "^6.1.1", + "vite-tsconfig-paths": "6.1.1", "vitest": "4.0.15" }, "engines": { @@ -85,9 +85,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", - "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.3.tgz", + "integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==", "dev": true, "license": "MIT", "dependencies": { @@ -692,29 +692,43 @@ } }, "node_modules/@humanfs/core": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", "dev": true, "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, "engines": { "node": ">=18.18.0" } }, "node_modules/@humanfs/node": { - "version": "0.16.7", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", - "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@humanfs/core": "^0.19.1", + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", "@humanwhocodes/retry": "^0.4.0" }, "engines": { "node": ">=18.18.0" } }, + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", @@ -782,9 +796,9 @@ } }, "node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.3.tgz", - "integrity": "sha512-xK9sGVbJWYb08+mTJt3/YV24WxvxpXcXtP6B172paPZ+Ts69Re9dAr7lKwJoeIx8OoeuimEiRZ7umkiUVClmmQ==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", + "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==", "license": "MIT", "optional": true, "dependencies": { @@ -799,6 +813,19 @@ "@emnapi/runtime": "^1.7.1" } }, + "node_modules/@nodable/entities": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@nodable/entities/-/entities-2.1.0.tgz", + "integrity": "sha512-nyT7T3nbMyBI/lvr6L5TyWbFJAI9FTgVRakNoBqCD+PmID8DzFrrNdLLtHMwMszOtqZa8PAOV24ZqDnQrhQINA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/nodable" + } + ], + "license": "MIT" + }, "node_modules/@oxc-project/types": { "version": "0.124.0", "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.124.0.tgz", @@ -1080,9 +1107,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.1.tgz", - "integrity": "sha512-d6FinEBLdIiK+1uACUttJKfgZREXrF0Qc2SmLII7W2AD8FfiZ9Wjd+rD/iRuf5s5dWrr1GgwXCvPqOuDquOowA==", + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.3.tgz", + "integrity": "sha512-x35CNW/ANXG3hE/EZpRU8MXX1JDN86hBb2wMGAtltkz7pc6cxgjpy1OMMfDosOQ+2hWqIkag/fGok1Yady9nGw==", "cpu": [ "arm" ], @@ -1094,9 +1121,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.1.tgz", - "integrity": "sha512-YjG/EwIDvvYI1YvYbHvDz/BYHtkY4ygUIXHnTdLhG+hKIQFBiosfWiACWortsKPKU/+dUwQQCKQM3qrDe8c9BA==", + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.3.tgz", + "integrity": "sha512-xw3xtkDApIOGayehp2+Rz4zimfkaX65r4t47iy+ymQB2G4iJCBBfj0ogVg5jpvjpn8UWn/+q9tprxleYeNp3Hw==", "cpu": [ "arm64" ], @@ -1108,9 +1135,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.1.tgz", - "integrity": "sha512-mjCpF7GmkRtSJwon+Rq1N8+pI+8l7w5g9Z3vWj4T7abguC4Czwi3Yu/pFaLvA3TTeMVjnu3ctigusqWUfjZzvw==", + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.3.tgz", + "integrity": "sha512-vo6Y5Qfpx7/5EaamIwi0WqW2+zfiusVihKatLvtN1VFVy3D13uERk/6gZLU1UiHRL6fDXqj/ELIeVRGnvcTE1g==", "cpu": [ "arm64" ], @@ -1122,9 +1149,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.1.tgz", - "integrity": "sha512-haZ7hJ1JT4e9hqkoT9R/19XW2QKqjfJVv+i5AGg57S+nLk9lQnJ1F/eZloRO3o9Scy9CM3wQ9l+dkXtcBgN5Ew==", + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.3.tgz", + "integrity": "sha512-D+0QGcZhBzTN82weOnsSlY7V7+RMmPuF1CkbxyMAGE8+ZHeUjyb76ZiWmBlCu//AQQONvxcqRbwZTajZKqjuOw==", "cpu": [ "x64" ], @@ -1136,9 +1163,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.1.tgz", - "integrity": "sha512-czw90wpQq3ZsAVBlinZjAYTKduOjTywlG7fEeWKUA7oCmpA8xdTkxZZlwNJKWqILlq0wehoZcJYfBvOyhPTQ6w==", + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.3.tgz", + "integrity": "sha512-6HnvHCT7fDyj6R0Ph7A6x8dQS/S38MClRWeDLqc0MdfWkxjiu1HSDYrdPhqSILzjTIC/pnXbbJbo+ft+gy/9hQ==", "cpu": [ "arm64" ], @@ -1150,9 +1177,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.1.tgz", - "integrity": "sha512-KVB2rqsxTHuBtfOeySEyzEOB7ltlB/ux38iu2rBQzkjbwRVlkhAGIEDiiYnO2kFOkJp+Z7pUXKyrRRFuFUKt+g==", + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.3.tgz", + "integrity": "sha512-KHLgC3WKlUYW3ShFKnnosZDOJ0xjg9zp7au3sIm2bs/tGBeC2ipmvRh/N7JKi0t9Ue20C0dpEshi8WUubg+cnA==", "cpu": [ "x64" ], @@ -1164,9 +1191,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.1.tgz", - "integrity": "sha512-L+34Qqil+v5uC0zEubW7uByo78WOCIrBvci69E7sFASRl0X7b/MB6Cqd1lky/CtcSVTydWa2WZwFuWexjS5o6g==", + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.3.tgz", + "integrity": "sha512-DV6fJoxEYWJOvaZIsok7KrYl0tPvga5OZ2yvKHNNYyk/2roMLqQAbGhr78EQ5YhHpnhLKJD3S1WFusAkmUuV5g==", "cpu": [ "arm" ], @@ -1178,9 +1205,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.1.tgz", - "integrity": "sha512-n83O8rt4v34hgFzlkb1ycniJh7IR5RCIqt6mz1VRJD6pmhRi0CXdmfnLu9dIUS6buzh60IvACM842Ffb3xd6Gg==", + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.3.tgz", + "integrity": "sha512-mQKoJAzvuOs6F+TZybQO4GOTSMUu7v0WdxEk24krQ/uUxXoPTtHjuaUuPmFhtBcM4K0ons8nrE3JyhTuCFtT/w==", "cpu": [ "arm" ], @@ -1192,9 +1219,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.1.tgz", - "integrity": "sha512-Nql7sTeAzhTAja3QXeAI48+/+GjBJ+QmAH13snn0AJSNL50JsDqotyudHyMbO2RbJkskbMbFJfIJKWA6R1LCJQ==", + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.3.tgz", + "integrity": "sha512-Whjj2qoiJ6+OOJMGptTYazaJvjOJm+iKHpXQM1P3LzGjt7Ff++Tp7nH4N8J/BUA7R9IHfDyx4DJIflifwnbmIA==", "cpu": [ "arm64" ], @@ -1206,9 +1233,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.1.tgz", - "integrity": "sha512-+pUymDhd0ys9GcKZPPWlFiZ67sTWV5UU6zOJat02M1+PiuSGDziyRuI/pPue3hoUwm2uGfxdL+trT6Z9rxnlMA==", + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.3.tgz", + "integrity": "sha512-4YTNHKqGng5+yiZt3mg77nmyuCfmNfX4fPmyUapBcIk+BdwSwmCWGXOUxhXbBEkFHtoN5boLj/5NON+u5QC9tg==", "cpu": [ "arm64" ], @@ -1220,9 +1247,9 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.1.tgz", - "integrity": "sha512-VSvgvQeIcsEvY4bKDHEDWcpW4Yw7BtlKG1GUT4FzBUlEKQK0rWHYBqQt6Fm2taXS+1bXvJT6kICu5ZwqKCnvlQ==", + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.3.tgz", + "integrity": "sha512-SU3kNlhkpI4UqlUc2VXPGK9o886ZsSeGfMAX2ba2b8DKmMXq4AL7KUrkSWVbb7koVqx41Yczx6dx5PNargIrEA==", "cpu": [ "loong64" ], @@ -1234,9 +1261,9 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.1.tgz", - "integrity": "sha512-4LqhUomJqwe641gsPp6xLfhqWMbQV04KtPp7/dIp0nzPxAkNY1AbwL5W0MQpcalLYk07vaW9Kp1PBhdpZYYcEw==", + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.3.tgz", + "integrity": "sha512-6lDLl5h4TXpB1mTf2rQWnAk/LcXrx9vBfu/DT5TIPhvMhRWaZ5MxkIc8u4lJAmBo6klTe1ywXIUHFjylW505sg==", "cpu": [ "loong64" ], @@ -1248,9 +1275,9 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.1.tgz", - "integrity": "sha512-tLQQ9aPvkBxOc/EUT6j3pyeMD6Hb8QF2BTBnCQWP/uu1lhc9AIrIjKnLYMEroIz/JvtGYgI9dF3AxHZNaEH0rw==", + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.3.tgz", + "integrity": "sha512-BMo8bOw8evlup/8G+cj5xWtPyp93xPdyoSN16Zy90Q2QZ0ZYRhCt6ZJSwbrRzG9HApFabjwj2p25TUPDWrhzqQ==", "cpu": [ "ppc64" ], @@ -1262,9 +1289,9 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.1.tgz", - "integrity": "sha512-RMxFhJwc9fSXP6PqmAz4cbv3kAyvD1etJFjTx4ONqFP9DkTkXsAMU4v3Vyc5BgzC+anz7nS/9tp4obsKfqkDHg==", + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.3.tgz", + "integrity": "sha512-E0L8X1dZN1/Rph+5VPF6Xj2G7JJvMACVXtamTJIDrVI44Y3K+G8gQaMEAavbqCGTa16InptiVrX6eM6pmJ+7qA==", "cpu": [ "ppc64" ], @@ -1276,9 +1303,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.1.tgz", - "integrity": "sha512-QKgFl+Yc1eEk6MmOBfRHYF6lTxiiiV3/z/BRrbSiW2I7AFTXoBFvdMEyglohPj//2mZS4hDOqeB0H1ACh3sBbg==", + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.3.tgz", + "integrity": "sha512-oZJ/WHaVfHUiRAtmTAeo3DcevNsVvH8mbvodjZy7D5QKvCefO371SiKRpxoDcCxB3PTRTLayWBkvmDQKTcX/sw==", "cpu": [ "riscv64" ], @@ -1290,9 +1317,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.1.tgz", - "integrity": "sha512-RAjXjP/8c6ZtzatZcA1RaQr6O1TRhzC+adn8YZDnChliZHviqIjmvFwHcxi4JKPSDAt6Uhf/7vqcBzQJy0PDJg==", + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.3.tgz", + "integrity": "sha512-Dhbyh7j9FybM3YaTgaHmVALwA8AkUwTPccyCQ79TG9AJUsMQqgN1DDEZNr4+QUfwiWvLDumW5vdwzoeUF+TNxQ==", "cpu": [ "riscv64" ], @@ -1304,9 +1331,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.1.tgz", - "integrity": "sha512-wcuocpaOlaL1COBYiA89O6yfjlp3RwKDeTIA0hM7OpmhR1Bjo9j31G1uQVpDlTvwxGn2nQs65fBFL5UFd76FcQ==", + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.3.tgz", + "integrity": "sha512-cJd1X5XhHHlltkaypz1UcWLA8AcoIi1aWhsvaWDskD1oz2eKCypnqvTQ8ykMNI0RSmm7NkTdSqSSD7zM0xa6Ig==", "cpu": [ "s390x" ], @@ -1318,9 +1345,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.1.tgz", - "integrity": "sha512-77PpsFQUCOiZR9+LQEFg9GClyfkNXj1MP6wRnzYs0EeWbPcHs02AXu4xuUbM1zhwn3wqaizle3AEYg5aeoohhg==", + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.3.tgz", + "integrity": "sha512-DAZDBHQfG2oQuhY7mc6I3/qB4LU2fQCjRvxbDwd/Jdvb9fypP4IJ4qmtu6lNjes6B531AI8cg1aKC2di97bUxA==", "cpu": [ "x64" ], @@ -1332,9 +1359,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.1.tgz", - "integrity": "sha512-5cIATbk5vynAjqqmyBjlciMJl1+R/CwX9oLk/EyiFXDWd95KpHdrOJT//rnUl4cUcskrd0jCCw3wpZnhIHdD9w==", + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.3.tgz", + "integrity": "sha512-cRxsE8c13mZOh3vP+wLDxpQBRrOHDIGOWyDL93Sy0Ga8y515fBcC2pjUfFwUe5T7tqvTvWbCpg1URM/AXdWIXA==", "cpu": [ "x64" ], @@ -1346,9 +1373,9 @@ ] }, "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.1.tgz", - "integrity": "sha512-cl0w09WsCi17mcmWqqglez9Gk8isgeWvoUZ3WiJFYSR3zjBQc2J5/ihSjpl+VLjPqjQ/1hJRcqBfLjssREQILw==", + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.3.tgz", + "integrity": "sha512-QaWcIgRxqEdQdhJqW4DJctsH6HCmo5vHxY0krHSX4jMtOqfzC+dqDGuHM87bu4H8JBeibWx7jFz+h6/4C8wA5Q==", "cpu": [ "x64" ], @@ -1360,9 +1387,9 @@ ] }, "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.1.tgz", - "integrity": "sha512-4Cv23ZrONRbNtbZa37mLSueXUCtN7MXccChtKpUnQNgF010rjrjfHx3QxkS2PI7LqGT5xXyYs1a7LbzAwT0iCA==", + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.3.tgz", + "integrity": "sha512-AaXwSvUi3QIPtroAUw1t5yHGIyqKEXwH54WUocFolZhpGDruJcs8c+xPNDRn4XiQsS7MEwnYsHW2l0MBLDMkWg==", "cpu": [ "arm64" ], @@ -1374,9 +1401,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.1.tgz", - "integrity": "sha512-i1okWYkA4FJICtr7KpYzFpRTHgy5jdDbZiWfvny21iIKky5YExiDXP+zbXzm3dUcFpkEeYNHgQ5fuG236JPq0g==", + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.3.tgz", + "integrity": "sha512-65LAKM/bAWDqKNEelHlcHvm2V+Vfb8C6INFxQXRHCvaVN1rJfwr4NvdP4FyzUaLqWfaCGaadf6UbTm8xJeYfEg==", "cpu": [ "arm64" ], @@ -1388,9 +1415,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.1.tgz", - "integrity": "sha512-u09m3CuwLzShA0EYKMNiFgcjjzwqtUMLmuCJLeZWjjOYA3IT2Di09KaxGBTP9xVztWyIWjVdsB2E9goMjZvTQg==", + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.3.tgz", + "integrity": "sha512-EEM2gyhBF5MFnI6vMKdX1LAosE627RGBzIoGMdLloPZkXrUN0Ckqgr2Qi8+J3zip/8NVVro3/FjB+tjhZUgUHA==", "cpu": [ "ia32" ], @@ -1402,9 +1429,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.1.tgz", - "integrity": "sha512-k+600V9Zl1CM7eZxJgMyTUzmrmhB/0XZnF4pRypKAlAgxmedUA+1v9R+XOFv56W4SlHEzfeMtzujLJD22Uz5zg==", + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.3.tgz", + "integrity": "sha512-E5Eb5H/DpxaoXH++Qkv28RcUJboMopmdDUALBczvHMf7hNIxaDZqwY5lK12UK1BHacSmvupoEWGu+n993Z0y1A==", "cpu": [ "x64" ], @@ -1416,9 +1443,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.1.tgz", - "integrity": "sha512-lWMnixq/QzxyhTV6NjQJ4SFo1J6PvOX8vUx5Wb4bBPsEb+8xZ89Bz6kOXpfXj9ak9AHTQVQzlgzBEc1SyM27xQ==", + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.3.tgz", + "integrity": "sha512-hPt/bgL5cE+Qp+/TPHBqptcAgPzgj46mPcg/16zNUmbQk0j+mOEQV/+Lqu8QRtDV3Ek95Q6FeFITpuhl6OTsAA==", "cpu": [ "x64" ], @@ -1437,9 +1464,9 @@ "license": "MIT" }, "node_modules/@tybys/wasm-util": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", - "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", + "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", "license": "MIT", "optional": true, "dependencies": { @@ -1472,9 +1499,9 @@ "license": "MIT" }, "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", "dev": true, "license": "MIT" }, @@ -1891,14 +1918,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.58.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.58.1.tgz", - "integrity": "sha512-gfQ8fk6cxhtptek+/8ZIqw8YrRW5048Gug8Ts5IYcMLCw18iUgrZAEY/D7s4hkI0FxEfGakKuPK/XUMPzPxi5g==", + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.2.tgz", + "integrity": "sha512-+2hqvEkeyf/0FBor67duF0Ll7Ot8jyKzDQOSrxazF/danillRq2DwR9dLptsXpoZQqxE1UisSmoZewrlPas9Vw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.58.1", - "@typescript-eslint/types": "^8.58.1", + "@typescript-eslint/tsconfig-utils": "^8.59.2", + "@typescript-eslint/types": "^8.59.2", "debug": "^4.4.3" }, "engines": { @@ -1913,14 +1940,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.58.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.58.1.tgz", - "integrity": "sha512-TPYUEqJK6avLcEjumWsIuTpuYODTTDAtoMdt8ZZa93uWMTX13Nb8L5leSje1NluammvU+oI3QRr5lLXPgihX3w==", + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.2.tgz", + "integrity": "sha512-JzfyEpEtOU89CcFSwyNS3mu4MLvLSXqnmX05+aKBDM+TdR5jzcGOEBwxwGNxrEQ7p/z6kK2WyioCGBf2zZBnvg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.58.1", - "@typescript-eslint/visitor-keys": "8.58.1" + "@typescript-eslint/types": "8.59.2", + "@typescript-eslint/visitor-keys": "8.59.2" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1931,9 +1958,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.58.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.58.1.tgz", - "integrity": "sha512-JAr2hOIct2Q+qk3G+8YFfqkqi7sC86uNryT+2i5HzMa2MPjw4qNFvtjnw1IiA1rP7QhNKVe21mSSLaSjwA1Olw==", + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.2.tgz", + "integrity": "sha512-BKK4alN7oi4C/zv4VqHQ+uRU+lTa6JGIZ7s1juw7b3RHo9OfKB+bKX3u0iVZetdsUCBBkSbdWbarJbmN0fTeSw==", "dev": true, "license": "MIT", "engines": { @@ -2127,9 +2154,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.58.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.58.1.tgz", - "integrity": "sha512-io/dV5Aw5ezwzfPBBWLoT+5QfVtP8O7q4Kftjn5azJ88bYyp/ZMCsyW1lpKK46EXJcaYMZ1JtYj+s/7TdzmQMw==", + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.2.tgz", + "integrity": "sha512-e82GVOE8Ps3E++Egvb6Y3Dw0S10u8NkQ9KXmtRhCWJJ8kDhOJTvtMAWnFL16kB1583goCWXsr0NieKCZMs2/0Q==", "dev": true, "license": "MIT", "engines": { @@ -2141,16 +2168,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.58.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.58.1.tgz", - "integrity": "sha512-w4w7WR7GHOjqqPnvAYbazq+Y5oS68b9CzasGtnd6jIeOIeKUzYzupGTB2T4LTPSv4d+WPeccbxuneTFHYgAAWg==", + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.2.tgz", + "integrity": "sha512-o0XPGNwcWw+FIwStOWn+BwBuEmL6QXP0rsvAFg7ET1dey1Nr6Wb1ac8p5HEsK0ygO/6mUxlk+YWQD9xcb/nnXg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.58.1", - "@typescript-eslint/tsconfig-utils": "8.58.1", - "@typescript-eslint/types": "8.58.1", - "@typescript-eslint/visitor-keys": "8.58.1", + "@typescript-eslint/project-service": "8.59.2", + "@typescript-eslint/tsconfig-utils": "8.59.2", + "@typescript-eslint/types": "8.59.2", + "@typescript-eslint/visitor-keys": "8.59.2", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", @@ -2169,16 +2196,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.58.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.58.1.tgz", - "integrity": "sha512-Ln8R0tmWC7pTtLOzgJzYTXSCjJ9rDNHAqTaVONF4FEi2qwce8mD9iSOxOpLFFvWp/wBFlew0mjM1L1ihYWfBdQ==", + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.59.2.tgz", + "integrity": "sha512-Juw3EinkXqjaffxz6roowvV7GZT/kET5vSKKZT6upl5TXdWkLkYmNPXwDDL2Vkt2DPn0nODIS4egC/0AGxKo/Q==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.58.1", - "@typescript-eslint/types": "8.58.1", - "@typescript-eslint/typescript-estree": "8.58.1" + "@typescript-eslint/scope-manager": "8.59.2", + "@typescript-eslint/types": "8.59.2", + "@typescript-eslint/typescript-estree": "8.59.2" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2193,13 +2220,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.58.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.58.1.tgz", - "integrity": "sha512-y+vH7QE8ycjoa0bWciFg7OpFcipUuem1ujhrdLtq1gByKwfbC7bPeKsiny9e0urg93DqwGcHey+bGRKCnF1nZQ==", + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.2.tgz", + "integrity": "sha512-NwjLUnGy8/Zfx23fl50tRC8rYaYnM52xNRYFAXvmiil9yh1+K6aRVQMnzW6gQB/1DLgWt977lYQn7C+wtgXZiA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.58.1", + "@typescript-eslint/types": "8.59.2", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -2434,9 +2461,9 @@ "license": "MIT" }, "node_modules/ajv": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", - "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", "dev": true, "license": "MIT", "dependencies": { @@ -3664,9 +3691,9 @@ "license": "MIT" }, "node_modules/fast-xml-builder": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.1.4.tgz", - "integrity": "sha512-f2jhpN4Eccy0/Uz9csxh3Nu6q4ErKxf0XIsasomfOihuSUa3/xw6w8dnOtCDgEItQFJG8KyXPzQXzcODDrrbOg==", + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.1.9.tgz", + "integrity": "sha512-jcyKVSEX13iseJqg7n/KWw+xnu/7fdrZ333Fac54KjHDIELVCfDDJXYIm6DTJ0Su4gSzrhqiK0DzY/wZbF40mw==", "dev": true, "funding": [ { @@ -3680,9 +3707,9 @@ } }, "node_modules/fast-xml-parser": { - "version": "5.5.12", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.5.12.tgz", - "integrity": "sha512-nUR0q8PPfoA/svPM43Gup7vLOZWppaNrYgGmrVqrAVJa7cOH4hMG6FX9M4mQ8dZA1/ObGZHzES7Ed88hxEBSJg==", + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.7.3.tgz", + "integrity": "sha512-C0AaNuC+mscy6vrAQKAc/rMq+zAPHodfHGZu4sGVehvAQt/JLG1O5zEcYcXSY5zSqr4YVgxsB+pHXTq0i7eDlg==", "dev": true, "funding": [ { @@ -3692,7 +3719,8 @@ ], "license": "MIT", "dependencies": { - "fast-xml-builder": "^1.1.4", + "@nodable/entities": "^2.1.0", + "fast-xml-builder": "^1.1.7", "path-expression-matcher": "^1.5.0", "strnum": "^2.2.3" }, @@ -4010,9 +4038,9 @@ } }, "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", + "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", "dev": true, "license": "MIT", "dependencies": { @@ -4106,13 +4134,13 @@ "license": "MIT" }, "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", + "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", "dev": true, "license": "MIT", "dependencies": { - "hasown": "^2.0.2" + "hasown": "^2.0.3" }, "engines": { "node": ">= 0.4" @@ -5021,9 +5049,9 @@ "license": "MIT" }, "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", "dev": true, "funding": [ { @@ -5316,9 +5344,9 @@ } }, "node_modules/postcss": { - "version": "8.5.9", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.9.tgz", - "integrity": "sha512-7a70Nsot+EMX9fFU3064K/kdHWZqGVY+BADLyXc8Dfv+mTLLVl6JzJpPaCZ2kQL9gIJvKXSLMHhqdRRjwQeFtw==", + "version": "8.5.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.14.tgz", + "integrity": "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==", "dev": true, "funding": [ { @@ -5648,9 +5676,9 @@ } }, "node_modules/rollup": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.1.tgz", - "integrity": "sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w==", + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.3.tgz", + "integrity": "sha512-pAQK9HalE84QSm4Po3EmWIZPd3FnjkShVkiMlz1iligWYkWQ7wHYd1PF/T7QZ5TVSD6uSTon5gBVMSM4JfBV+A==", "dev": true, "license": "MIT", "dependencies": { @@ -5664,34 +5692,41 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.60.1", - "@rollup/rollup-android-arm64": "4.60.1", - "@rollup/rollup-darwin-arm64": "4.60.1", - "@rollup/rollup-darwin-x64": "4.60.1", - "@rollup/rollup-freebsd-arm64": "4.60.1", - "@rollup/rollup-freebsd-x64": "4.60.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.60.1", - "@rollup/rollup-linux-arm-musleabihf": "4.60.1", - "@rollup/rollup-linux-arm64-gnu": "4.60.1", - "@rollup/rollup-linux-arm64-musl": "4.60.1", - "@rollup/rollup-linux-loong64-gnu": "4.60.1", - "@rollup/rollup-linux-loong64-musl": "4.60.1", - "@rollup/rollup-linux-ppc64-gnu": "4.60.1", - "@rollup/rollup-linux-ppc64-musl": "4.60.1", - "@rollup/rollup-linux-riscv64-gnu": "4.60.1", - "@rollup/rollup-linux-riscv64-musl": "4.60.1", - "@rollup/rollup-linux-s390x-gnu": "4.60.1", - "@rollup/rollup-linux-x64-gnu": "4.60.1", - "@rollup/rollup-linux-x64-musl": "4.60.1", - "@rollup/rollup-openbsd-x64": "4.60.1", - "@rollup/rollup-openharmony-arm64": "4.60.1", - "@rollup/rollup-win32-arm64-msvc": "4.60.1", - "@rollup/rollup-win32-ia32-msvc": "4.60.1", - "@rollup/rollup-win32-x64-gnu": "4.60.1", - "@rollup/rollup-win32-x64-msvc": "4.60.1", + "@rollup/rollup-android-arm-eabi": "4.60.3", + "@rollup/rollup-android-arm64": "4.60.3", + "@rollup/rollup-darwin-arm64": "4.60.3", + "@rollup/rollup-darwin-x64": "4.60.3", + "@rollup/rollup-freebsd-arm64": "4.60.3", + "@rollup/rollup-freebsd-x64": "4.60.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.3", + "@rollup/rollup-linux-arm-musleabihf": "4.60.3", + "@rollup/rollup-linux-arm64-gnu": "4.60.3", + "@rollup/rollup-linux-arm64-musl": "4.60.3", + "@rollup/rollup-linux-loong64-gnu": "4.60.3", + "@rollup/rollup-linux-loong64-musl": "4.60.3", + "@rollup/rollup-linux-ppc64-gnu": "4.60.3", + "@rollup/rollup-linux-ppc64-musl": "4.60.3", + "@rollup/rollup-linux-riscv64-gnu": "4.60.3", + "@rollup/rollup-linux-riscv64-musl": "4.60.3", + "@rollup/rollup-linux-s390x-gnu": "4.60.3", + "@rollup/rollup-linux-x64-gnu": "4.60.3", + "@rollup/rollup-linux-x64-musl": "4.60.3", + "@rollup/rollup-openbsd-x64": "4.60.3", + "@rollup/rollup-openharmony-arm64": "4.60.3", + "@rollup/rollup-win32-arm64-msvc": "4.60.3", + "@rollup/rollup-win32-ia32-msvc": "4.60.3", + "@rollup/rollup-win32-x64-gnu": "4.60.3", + "@rollup/rollup-win32-x64-msvc": "4.60.3", "fsevents": "~2.3.2" } }, + "node_modules/rollup/node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, "node_modules/rxjs": { "version": "7.8.2", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", @@ -5940,9 +5975,9 @@ } }, "node_modules/strnum": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.2.3.tgz", - "integrity": "sha512-oKx6RUCuHfT3oyVjtnrmn19H1SiCqgJSg+54XqURKp5aCMbrXrhLjRN9TjuwMjiYstZ0MzDrHqkGZ5dFTKd+zg==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.3.0.tgz", + "integrity": "sha512-ums3KNd42PGyx5xaoVTO1mjU1bH3NpY4vsrVlnv9PNGqQj8wd7rJ6nEypLrJ7z5vxK5RP0yMLo6J/Gsm62DI5Q==", "dev": true, "funding": [ { @@ -6047,9 +6082,9 @@ "license": "MIT" }, "node_modules/tinyexec": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.1.1.tgz", - "integrity": "sha512-VKS/ZaQhhkKFMANmAOhhXVoIfBXblQxGX1myCQ2faQrfmobMftXeJPcZGp0gS07ocvGJWDLZGyOZDadDBqYIJg==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.1.2.tgz", + "integrity": "sha512-dAqSqE/RabpBKI8+h26GfLq6Vb3JVXs30XYQjdMjaj/c2tS8IYYMbIzP599KtRj7c57/wYApb3QjgRgXmrCukA==", "dev": true, "license": "MIT", "engines": { @@ -6424,9 +6459,9 @@ } }, "node_modules/vite": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.2.tgz", - "integrity": "sha512-Bby3NOsna2jsjfLVOHKes8sGwgl4TT0E6vvpYgnAYDIF/tie7MRaFthmKuHx1NSXjiTueXH3do80FMQgvEktRg==", + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.3.tgz", + "integrity": "sha512-/4XH147Ui7OGTjg3HbdWe5arnZQSbfuRzdr9Ec7TQi5I7R+ir0Rlc9GIvD4v0XZurELqA035KVXJXpR61xhiTA==", "dev": true, "license": "MIT", "dependencies": { @@ -6685,9 +6720,9 @@ "license": "ISC" }, "node_modules/yaml": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz", - "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==", + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.4.tgz", + "integrity": "sha512-ml/JPOj9fOQK8RNnWojA67GbZ0ApXAUlN2UQclwv2eVgTgn7O9gg9o7paZWKMp4g0H3nTLtS9LVzhkpOFIKzog==", "dev": true, "license": "ISC", "bin": { diff --git a/package.json b/package.json index 45eb694..6f4ff9a 100644 --- a/package.json +++ b/package.json @@ -53,11 +53,12 @@ "cli:node.rx:cache": "./bin/cli.js test/openapi.json --target node.rx --cache", "prettier": "prettier . --check", "prettier:fix": "prettier . --write", - "test": "vitest run", "test:coverage": "vitest run --coverage", + "test:generator": "vitest run src/", "lint": "eslint ./src --cache", "lint:fix": "npm run lint -- --fix", - "ci": "npm run prettier && npm run lint && npm run test && npm run build && npm run cli:browser:v1 && npm run cli:browser:v2 && npm run cli:browser:v3", + "ci:test": "./scripts/run-test-configuration.js --version v3", + "ci": "npm run prettier && npm run lint && npm run build && npm run ci:test", "release": "standard-version" }, "devDependencies": { @@ -68,7 +69,7 @@ "eslint": "10.0.2", "prettier": "3.3.3", "typescript-eslint": "8.56.1", - "vite-tsconfig-paths": "^6.1.1", + "vite-tsconfig-paths": "6.1.1", "vitest": "4.0.15" }, "dependencies": { @@ -80,7 +81,7 @@ "indent-string": "5.0.0", "openapi-types": "12.1.3", "pretty-ms": "8.0.0", - "rolldown": "^1.0.0-rc.10", + "rolldown": "1.0.0-rc.15", "typescript": "5.9.3", "yargs": "17.7.2" }, diff --git a/scripts/run-test-configuration.js b/scripts/run-test-configuration.js new file mode 100644 index 0000000..3a25ca0 --- /dev/null +++ b/scripts/run-test-configuration.js @@ -0,0 +1,116 @@ +#!/usr/bin/env node +// @ts-check + +import { execSync } from 'node:child_process'; +import { existsSync, writeFileSync } from 'node:fs'; +import { resolve } from 'node:path'; + +/** @param {string} flag */ +const hasFlag = (flag) => process.argv.includes(flag); + +/** + * @param {string} flag + * @param {string} [defaultValue] + */ +const getFlagValue = (flag, defaultValue) => { + const idx = process.argv.indexOf(flag); + return idx !== -1 ? process.argv[idx + 1] : defaultValue; +}; + +/** @param {string} cmd */ +const run = (cmd) => { + console.log(`\n> ${cmd}`); + execSync(cmd, { stdio: 'inherit' }); +}; + +const version = getFlagValue('--version'); +if (!version) { + console.error('Error: --version is required (e.g. --version v1)'); + process.exit(1); +} + +const target = getFlagValue('--target', 'browser'); + +const validTargets = ['browser', 'browser.rx', 'node', 'node.rx']; +if (!validTargets.includes(target)) { + console.error(`Error: --target must be one of ${validTargets.join(', ')} (got "${target}")`); + process.exit(1); +} + +const useQueryLanguage = hasFlag('--use-query-language'); +const deprecated = hasFlag('--deprecated'); +const generateUnique = hasFlag('--generate-unique'); + +const openapiFile = version === 'v1' ? 'test/openapi.json' : `test/openapi_${version}.json`; + +if (!existsSync(resolve(openapiFile))) { + console.error(`Error: OpenAPI file not found: ${openapiFile}`); + process.exit(1); +} + +const resolvedTarget = target.replace('.', '-'); + +/** @type {string[]} */ +const testFolders = []; + +if (!useQueryLanguage && !deprecated && !generateUnique && target === 'browser') { + testFolders.push('unparameterized'); +} +if (useQueryLanguage) { + testFolders.push('use-query-language'); +} +if (deprecated) { + testFolders.push('deprecated'); +} +if (generateUnique) { + testFolders.push('generate-unique'); +} +if (target !== 'browser') { + testFolders.push(`target/${resolvedTarget}`); +} + +// --- Build CLI args for SDK generation --- + +/** @type {string[]} */ +const cliArgs = [`--target ${target}`]; + +if (useQueryLanguage) { + cliArgs.push('--use-query-language'); +} +if (deprecated) { + cliArgs.push('--deprecated'); +} +if (generateUnique) { + cliArgs.push('--generate-unique'); +} + +console.log(''); +console.log('run-combo configuration:'); +console.log(` version : ${version}`); +console.log(` target : ${target}`); +console.log(` use-query-language: ${useQueryLanguage}`); +console.log(` deprecated : ${deprecated}`); +console.log(` generate-unique : ${generateUnique}`); +console.log(` test folders : ${testFolders.join(', ')}`); +console.log(''); + +// --- Step 1: Generate SDK --- + +run(`node ./bin/cli.js ${openapiFile} ${cliArgs.join(' ')}`); + +// --- Step 2: Generate temporary tsconfig for type-checking --- + +const tsconfigIncludes = testFolders.map((folder) => `test/${version}/${folder}/**/*.spec.ts`); +const tsconfigContent = JSON.stringify({ extends: './tsconfig.node.json', include: tsconfigIncludes }, null, 2) + '\n'; +writeFileSync(resolve('tsconfig.typecheck-generated.json'), tsconfigContent); +console.log(`\nGenerated tsconfig.typecheck-generated.json`); +console.log(` includes: ${tsconfigIncludes.join(', ')}`); + +// --- Step 3: Type-check generated test files --- + +run(`npx tsc -p tsconfig.typecheck-generated.json --noEmit --skipLibCheck`); + +// --- Step 4: Run vitest for all resolved folders at the given version --- + +const testPaths = testFolders.map((folder) => `test/${version}/${folder}`).join(' '); +run(`npx vitest run ${testPaths}`); diff --git a/src/generator/01-base/static/queriesWithQueryLanguage.ts.txt b/src/generator/01-base/static/queriesWithQueryLanguage.ts.txt index eaa8da8..472e3e4 100644 --- a/src/generator/01-base/static/queriesWithQueryLanguage.ts.txt +++ b/src/generator/01-base/static/queriesWithQueryLanguage.ts.txt @@ -160,20 +160,20 @@ export type FieldOrderBy = { export type OrderBy = FieldOrderBy | ConditionalOrderBy; export type CountQuery = { - where?: QueryFilter; + where?: QueryFilter | string; }; type SomeQueryBase = { serializeNulls?: boolean; include?: I; properties?: P; - where?: QueryFilter; + where?: QueryFilter | string; select?: QuerySelect; pagination?: Pagination; }; export type SomeQuery = SomeQueryBase & - ({ sort?: Sort[]; orderBy?: never } | { sort?: never; orderBy?: OrderBy[] }); + ({ sort?: Sort[]; orderBy?: never } | { sort?: never; orderBy?: OrderBy[] | string }); const comparisonOperatorList: ComparisonOperator[] = [ 'EQ', @@ -311,10 +311,15 @@ const evaluateCaseExpression = (exp: QueryFilter, nestedPaths: string[]): s return `(${ifExp} ? ${thenBranch} : ${elseBranch})`; }; -const assembleOrderBy = (orderBy: OrderBy[] = [], usePost?: boolean): Record => { - if(!orderBy.length) { - return {} +const assembleOrderBy = (orderBy?: OrderBy[] | string, usePost?: boolean): Record => { + if (typeof orderBy === 'string') { + return { orderBy: [orderBy] }; } + + if (!orderBy || (Array.isArray(orderBy) && !orderBy.length)) { + return {}; + } + const flattedOrderByList = flattenOrderBy(orderBy); if(!flattedOrderByList.length) { @@ -330,9 +335,17 @@ const assembleOrderBy = (orderBy: OrderBy[] = [], usePost?: boolean): Recor } const assembleFilterParam = ( - obj: QueryFilter = {} + filter?: QueryFilter | string ): Record => { - const flattedFilter = flattenWhere(obj, []); + if(!filter) { + return {}; + } + + if (typeof filter === 'string') { + return { filter }; + } + + const flattedFilter = flattenWhere(filter, []); return flattedFilter.length ? { filter: flattedFilter.join(' and ') } : {}; }; diff --git a/test/v3/deprecated/tests.spec.ts b/test/v3/deprecated/tests.spec.ts new file mode 100644 index 0000000..a9cdcf5 --- /dev/null +++ b/test/v3/deprecated/tests.spec.ts @@ -0,0 +1,3 @@ +import { describe } from 'vitest'; + +describe('add a test', () => {}); diff --git a/test/v3/generate-unique/tests.spec.ts b/test/v3/generate-unique/tests.spec.ts new file mode 100644 index 0000000..a9cdcf5 --- /dev/null +++ b/test/v3/generate-unique/tests.spec.ts @@ -0,0 +1,3 @@ +import { describe } from 'vitest'; + +describe('add a test', () => {}); diff --git a/test/v3/target/browser-rx/tests.spec.ts b/test/v3/target/browser-rx/tests.spec.ts new file mode 100644 index 0000000..847dc14 --- /dev/null +++ b/test/v3/target/browser-rx/tests.spec.ts @@ -0,0 +1,5 @@ +import { describe, it } from 'vitest'; + +describe('some test', () => { + it('add a test', () => {}); +}); diff --git a/test/v3/unparameterized/tests.spec.ts b/test/v3/unparameterized/tests.spec.ts new file mode 100644 index 0000000..ba095b9 --- /dev/null +++ b/test/v3/unparameterized/tests.spec.ts @@ -0,0 +1,105 @@ +import { afterAll, beforeAll, describe, expect, it } from 'vitest'; +import { articleService, setGlobalConfig } from '@sdk/dist'; + +describe('filter', () => { + let capturedRequest: Request; + + beforeAll(() => { + setGlobalConfig({ + host: 'test.example.com', + secure: true, + key: 'test-key', + interceptors: { + request: (request) => { + capturedRequest = request; + return new Response( + JSON.stringify({ + result: [], + referencedEntities: {}, + additionalProperties: {} + }), + { + status: 200, + headers: { 'content-type': 'application/json' } + } + ); + } + } + }); + }); + + describe('some query', () => { + it('should assemble a typed filter object into individual query params', async () => { + const service = articleService(); + + await service.some({ + filter: { + articleNumber: { EQ: 'ART-001' } + } + }); + + const url = new URL(capturedRequest.url); + expect(url.searchParams.get('articleNumber-eq')).toBe('ART-001'); + }); + + it('should pass sort as a comma-separated query param', async () => { + const service = articleService(); + + await service.some({ + sort: [{ articleNumber: 'asc' }, { name: 'desc' }] + }); + + const url = new URL(capturedRequest.url); + expect(url.searchParams.get('sort')).toBe('articleNumber,-name'); + }); + + it('should pass pagination as page and pageSize query params', async () => { + const service = articleService(); + + await service.some({ + pagination: { page: 3, pageSize: 25 } + }); + + const url = new URL(capturedRequest.url); + expect(url.searchParams.get('page')).toBe('3'); + expect(url.searchParams.get('pageSize')).toBe('25'); + }); + + it('should combine filter, sort and pagination into a single request', async () => { + const service = articleService(); + + await service.some({ + filter: { + articleNumber: { EQ: 'ART-001' } + }, + sort: [{ articleNumber: 'asc' }], + pagination: { page: 1, pageSize: 10 } + }); + + const url = new URL(capturedRequest.url); + expect(url.searchParams.get('articleNumber-eq')).toBe('ART-001'); + expect(url.searchParams.get('sort')).toBe('articleNumber'); + expect(url.searchParams.get('page')).toBe('1'); + expect(url.searchParams.get('pageSize')).toBe('10'); + }); + }); + + describe('count query', () => { + it('should assemble a typed filter object into individual query params', async () => { + const service = articleService(); + + await service.count({ + filter: { + articleNumber: { EQ: 'ART-001' } + } + }); + + const url = new URL(capturedRequest.url); + expect(url.searchParams.get('articleNumber-eq')).toBe('ART-001'); + }); + }); + + afterAll(() => { + setGlobalConfig(undefined); + }); +}); diff --git a/test/v3/use-query-language/tests.spec.ts b/test/v3/use-query-language/tests.spec.ts new file mode 100644 index 0000000..b9e3827 --- /dev/null +++ b/test/v3/use-query-language/tests.spec.ts @@ -0,0 +1,197 @@ +import { afterAll, beforeAll, describe, expect, it } from 'vitest'; +import { articleService, setGlobalConfig } from '@sdk/dist'; + +describe('where', () => { + let capturedRequest: Request; + + beforeAll(() => { + setGlobalConfig({ + host: 'test.example.com', + secure: true, + key: 'test-key', + interceptors: { + request: (request) => { + capturedRequest = request; + return new Response( + JSON.stringify({ + result: [], + referencedEntities: {}, + additionalProperties: {} + }), + { + status: 200, + headers: { 'content-type': 'application/json' } + } + ); + } + } + }); + }); + + describe('where filter', () => { + it('should assemble a typed where object into a filter query param', async () => { + const service = articleService(); + + await service.some({ + where: { + articleNumber: { EQ: 'ART-001' } + } + }); + + const url = new URL(capturedRequest.url); + expect(url.searchParams.get('filter')).toBe('articleNumber = "ART-001"'); + }); + + it('should pass a raw string where directly as the filter query param', async () => { + const service = articleService(); + + await service.some({ + where: 'articleNumber = "ART-001"' + }); + + const url = new URL(capturedRequest.url); + expect(url.searchParams.get('filter')).toBe('articleNumber = "ART-001"'); + }); + + it('should pass sort as a comma-separated query param', async () => { + const service = articleService(); + + await service.some({ + sort: [{ articleNumber: 'asc' }, { name: 'desc' }] + }); + + const url = new URL(capturedRequest.url); + expect(url.searchParams.get('sort')).toBe('articleNumber,-name'); + }); + + it('should pass pagination as page and pageSize query params', async () => { + const service = articleService(); + + await service.some({ + pagination: { page: 3, pageSize: 25 } + }); + + const url = new URL(capturedRequest.url); + expect(url.searchParams.get('page')).toBe('3'); + expect(url.searchParams.get('pageSize')).toBe('25'); + }); + + it('should assemble a typed where object into the request body when usePost is true', async () => { + const service = articleService({ usePost: true }); + + await service.some({ + where: { + articleNumber: { EQ: 'ART-001' } + } + }); + + const body = await capturedRequest.json(); + expect(body.filter).toBe('articleNumber = "ART-001"'); + }); + + it('should pass a raw string where directly into the request body when usePost is true', async () => { + const service = articleService({ usePost: true }); + + await service.some({ + where: 'articleNumber = "ART-001"' + }); + + const body = await capturedRequest.json(); + expect(body.filter).toBe('articleNumber = "ART-001"'); + }); + + it('should combine where, sort and pagination into a single request', async () => { + const service = articleService(); + + await service.some({ + where: 'articleNumber = "ART-001"', + sort: [{ articleNumber: 'asc' }], + pagination: { page: 1, pageSize: 10 } + }); + + const url = new URL(capturedRequest.url); + expect(url.searchParams.get('filter')).toBe('articleNumber = "ART-001"'); + expect(url.searchParams.get('sort')).toBe('articleNumber'); + expect(url.searchParams.get('page')).toBe('1'); + expect(url.searchParams.get('pageSize')).toBe('10'); + }); + }); + + describe('orderBy', () => { + it('should assemble a typed orderBy object into an orderBy query param', async () => { + const service = articleService(); + + await service.some({ + orderBy: [{ FIELD: { articleNumber: true }, SORT: 'desc' }] + }); + + const url = new URL(capturedRequest.url); + expect(url.searchParams.get('orderBy')).toBe('articleNumber desc'); + }); + + it('should pass a raw string orderBy directly as the orderBy query param', async () => { + const service = articleService(); + + await service.some({ + orderBy: 'articleNumber desc' + }); + + const url = new URL(capturedRequest.url); + expect(url.searchParams.get('orderBy')).toBe('["articleNumber desc"]'); + }); + + it('should assemble a typed orderBy object into the request body when usePost is true', async () => { + const service = articleService({ usePost: true }); + + await service.some({ + orderBy: [{ FIELD: { articleNumber: true }, SORT: 'desc' }] + }); + + const body = await capturedRequest.json(); + expect(body.orderBy).toEqual(['articleNumber desc']); + }); + + it('should pass a raw string orderBy directly into the request body when usePost is true', async () => { + const service = articleService({ usePost: true }); + + await service.some({ + orderBy: 'articleNumber desc' + }); + + const body = await capturedRequest.json(); + expect(body.orderBy).toEqual(['articleNumber desc']); + }); + + it('should pass pagination as page and pageSize query params', async () => { + const service = articleService(); + + await service.some({ + pagination: { page: 3, pageSize: 25 } + }); + + const url = new URL(capturedRequest.url); + expect(url.searchParams.get('page')).toBe('3'); + expect(url.searchParams.get('pageSize')).toBe('25'); + }); + + it('should combine where, orderBy and pagination into a single request', async () => { + const service = articleService(); + + await service.some({ + where: 'articleNumber = "ART-001"', + orderBy: 'articleNumber desc', + pagination: { page: 1, pageSize: 10 } + }); + + const url = new URL(capturedRequest.url); + expect(url.searchParams.get('filter')).toBe('articleNumber = "ART-001"'); + expect(url.searchParams.get('orderBy')).toBe('["articleNumber desc"]'); + expect(url.searchParams.get('page')).toBe('1'); + expect(url.searchParams.get('pageSize')).toBe('10'); + }); + }); + + afterAll(() => { + setGlobalConfig(undefined); + }); +}); diff --git a/tsconfig.json b/tsconfig.json index 06495b7..27a9587 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,6 +6,9 @@ }, { "path": "./tsconfig.sdk.json" + }, + { + "path": "./tsconfig.typecheck-generated.json" } ] } diff --git a/tsconfig.rollup.json b/tsconfig.rollup.json new file mode 100644 index 0000000..72cb0e6 --- /dev/null +++ b/tsconfig.rollup.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.node.json", + "include": ["src/**/*.ts", "rollup.config.ts"] +} diff --git a/tsconfig.typecheck-generated.json b/tsconfig.typecheck-generated.json new file mode 100644 index 0000000..81168d0 --- /dev/null +++ b/tsconfig.typecheck-generated.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.node.json", + "include": [] +} diff --git a/vitest.config.ts b/vitest.config.ts index 04e585b..0783fef 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -5,6 +5,6 @@ export default defineConfig({ plugins: [tsconfigPaths()], test: { globals: true, - include: ['src/**/*.spec.ts'] + include: ['src/**/*.spec.ts', 'test/**/*.spec.ts'] } });