diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 8f32b4daf..281a52027 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "4.61.1" + ".": "4.62.0" } diff --git a/CHANGELOG.md b/CHANGELOG.md index 1fc81d615..87fff3e15 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,21 @@ # Changelog +## 4.62.0 (2024-09-17) + +Full Changelog: [v4.61.1...v4.62.0](https://github.com/openai/openai-node/compare/v4.61.1...v4.62.0) + +### Features + +* **client:** add ._request_id property to object responses ([#1078](https://github.com/openai/openai-node/issues/1078)) ([d5c2131](https://github.com/openai/openai-node/commit/d5c21314449091dd1c668c7358b25e041466f588)) + + +### Chores + +* **internal:** add ecosystem test for qs reproduction ([0199dd8](https://github.com/openai/openai-node/commit/0199dd85981497fac2b60f786acc00ea30683897)) +* **internal:** add query string encoder ([#1079](https://github.com/openai/openai-node/issues/1079)) ([f870682](https://github.com/openai/openai-node/commit/f870682d5c490182547c428b0b5c75da0e34d15a)) +* **internal:** fix some types ([#1082](https://github.com/openai/openai-node/issues/1082)) ([1ec41a7](https://github.com/openai/openai-node/commit/1ec41a7d768502a31abb33bf86b0539e5b4b6541)) +* **tests:** add query string tests to ecosystem tests ([36be724](https://github.com/openai/openai-node/commit/36be724384401bb697d8b07b0a1965be721cfa51)) + ## 4.61.1 (2024-09-16) Full Changelog: [v4.61.0...v4.61.1](https://github.com/openai/openai-node/compare/v4.61.0...v4.61.1) diff --git a/README.md b/README.md index 03ee259ee..88774c05b 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ You can import in Deno via: ```ts -import OpenAI from 'https://deno.land/x/openai@v4.61.1/mod.ts'; +import OpenAI from 'https://deno.land/x/openai@v4.62.0/mod.ts'; ``` @@ -361,6 +361,17 @@ Error codes are as followed: | >=500 | `InternalServerError` | | N/A | `APIConnectionError` | +## Request IDs + +> For more information on debugging requests, see [these docs](https://platform.openai.com/docs/api-reference/debugging-requests) + +All object responses in the SDK provide a `_request_id` property which is added from the `x-request-id` response header so that you can quickly log failing requests and report them back to OpenAI. + +```ts +const completion = await client.chat.completions.create({ messages: [{ role: 'user', content: 'Say this is a test' }], model: 'gpt-4' }); +console.log(completion._request_id) // req_123 +``` + ## Microsoft Azure OpenAI To use this library with [Azure OpenAI](https://learn.microsoft.com/azure/ai-services/openai/overview), use the `AzureOpenAI` diff --git a/ecosystem-tests/cli.ts b/ecosystem-tests/cli.ts index 2d9702112..550512634 100644 --- a/ecosystem-tests/cli.ts +++ b/ecosystem-tests/cli.ts @@ -34,6 +34,14 @@ const projectRunners = { await installPackage(); await run('node', ['test.js']); }, + 'nodenext-tsup': async () => { + await installPackage(); + await run('npm', ['run', 'build']); + + if (state.live) { + await run('npm', ['run', 'main']); + } + }, 'ts-browser-webpack': async () => { await installPackage(); diff --git a/ecosystem-tests/node-js/test.js b/ecosystem-tests/node-js/test.js index 7f9f21736..e2a26f856 100644 --- a/ecosystem-tests/node-js/test.js +++ b/ecosystem-tests/node-js/test.js @@ -1,8 +1,35 @@ -const openaiKey = "a valid OpenAI key" const OpenAI = require('openai'); -console.log(OpenAI) +const openai = new OpenAI(); -const openai = new OpenAI({ - apiKey: openaiKey, -}); +function assertEqual(actual, expected) { + if (actual === expected) { + return; + } + + console.error('expected', expected); + console.error('actual ', actual); + throw new Error('expected values to be equal'); +} + +async function main() { + const completion = await openai.chat.completions.create({ + model: 'gpt-4', + messages: [{ role: 'user', content: 'Say this is a test' }], + }); + if (!completion.choices[0].message.content) { + console.dir(completion, { depth: 4 }); + throw new Error('no response content!'); + } + + assertEqual( + decodeURIComponent(openai.stringifyQuery({ foo: { nested: { a: true, b: 'foo' } } })), + 'foo[nested][a]=true&foo[nested][b]=foo', + ); + assertEqual( + decodeURIComponent(openai.stringifyQuery({ foo: { nested: { a: ['hello', 'world'] } } })), + 'foo[nested][a][]=hello&foo[nested][a][]=world', + ); +} + +main(); diff --git a/ecosystem-tests/node-ts-cjs-auto/tests/test.ts b/ecosystem-tests/node-ts-cjs-auto/tests/test.ts index 84c99ee5a..203afba5a 100644 --- a/ecosystem-tests/node-ts-cjs-auto/tests/test.ts +++ b/ecosystem-tests/node-ts-cjs-auto/tests/test.ts @@ -257,3 +257,12 @@ describe('toFile', () => { expect(result.filename).toEqual('finetune.jsonl'); }); }); + +test('query strings', () => { + expect( + decodeURIComponent((client as any).stringifyQuery({ foo: { nested: { a: true, b: 'foo' } } })), + ).toEqual('foo[nested][a]=true&foo[nested][b]=foo'); + expect( + decodeURIComponent((client as any).stringifyQuery({ foo: { nested: { a: ['hello', 'world'] } } })), + ).toEqual('foo[nested][a][]=hello&foo[nested][a][]=world'); +}); diff --git a/ecosystem-tests/node-ts-cjs-web/tests/test-jsdom.ts b/ecosystem-tests/node-ts-cjs-web/tests/test-jsdom.ts index adcb44858..e7b6be07d 100644 --- a/ecosystem-tests/node-ts-cjs-web/tests/test-jsdom.ts +++ b/ecosystem-tests/node-ts-cjs-web/tests/test-jsdom.ts @@ -164,3 +164,12 @@ describe.skip('toFile', () => { expect(result.filename).toEqual('finetune.jsonl'); }); }); + +test('query strings', () => { + expect( + decodeURIComponent((client as any).stringifyQuery({ foo: { nested: { a: true, b: 'foo' } } })), + ).toEqual('foo[nested][a]=true&foo[nested][b]=foo'); + expect( + decodeURIComponent((client as any).stringifyQuery({ foo: { nested: { a: ['hello', 'world'] } } })), + ).toEqual('foo[nested][a][]=hello&foo[nested][a][]=world'); +}); diff --git a/ecosystem-tests/node-ts-cjs-web/tests/test-node.ts b/ecosystem-tests/node-ts-cjs-web/tests/test-node.ts index 1784f8d5e..668e65332 100644 --- a/ecosystem-tests/node-ts-cjs-web/tests/test-node.ts +++ b/ecosystem-tests/node-ts-cjs-web/tests/test-node.ts @@ -151,3 +151,12 @@ describe('toFile', () => { expect(result.filename).toEqual('finetune.jsonl'); }); }); + +test('query strings', () => { + expect( + decodeURIComponent((client as any).stringifyQuery({ foo: { nested: { a: true, b: 'foo' } } })), + ).toEqual('foo[nested][a]=true&foo[nested][b]=foo'); + expect( + decodeURIComponent((client as any).stringifyQuery({ foo: { nested: { a: ['hello', 'world'] } } })), + ).toEqual('foo[nested][a][]=hello&foo[nested][a][]=world'); +}); diff --git a/ecosystem-tests/node-ts-cjs/tests/test-jsdom.ts b/ecosystem-tests/node-ts-cjs/tests/test-jsdom.ts index 9908e45f8..15b9df7c9 100644 --- a/ecosystem-tests/node-ts-cjs/tests/test-jsdom.ts +++ b/ecosystem-tests/node-ts-cjs/tests/test-jsdom.ts @@ -144,3 +144,12 @@ describe.skip('toFile', () => { expect(result.filename).toEqual('finetune.jsonl'); }); }); + +test('query strings', () => { + expect( + decodeURIComponent((client as any).stringifyQuery({ foo: { nested: { a: true, b: 'foo' } } })), + ).toEqual('foo[nested][a]=true&foo[nested][b]=foo'); + expect( + decodeURIComponent((client as any).stringifyQuery({ foo: { nested: { a: ['hello', 'world'] } } })), + ).toEqual('foo[nested][a][]=hello&foo[nested][a][]=world'); +}); diff --git a/ecosystem-tests/node-ts-cjs/tests/test-node.ts b/ecosystem-tests/node-ts-cjs/tests/test-node.ts index 5ece57019..3f6e5d572 100644 --- a/ecosystem-tests/node-ts-cjs/tests/test-node.ts +++ b/ecosystem-tests/node-ts-cjs/tests/test-node.ts @@ -192,3 +192,12 @@ describe('toFile', () => { expect(result.filename).toEqual('finetune.jsonl'); }); }); + +test('query strings', () => { + expect( + decodeURIComponent((client as any).stringifyQuery({ foo: { nested: { a: true, b: 'foo' } } })), + ).toEqual('foo[nested][a]=true&foo[nested][b]=foo'); + expect( + decodeURIComponent((client as any).stringifyQuery({ foo: { nested: { a: ['hello', 'world'] } } })), + ).toEqual('foo[nested][a][]=hello&foo[nested][a][]=world'); +}); diff --git a/ecosystem-tests/node-ts-esm-auto/tests/test.ts b/ecosystem-tests/node-ts-esm-auto/tests/test.ts index d28bc2b37..88beb2f54 100644 --- a/ecosystem-tests/node-ts-esm-auto/tests/test.ts +++ b/ecosystem-tests/node-ts-esm-auto/tests/test.ts @@ -194,3 +194,12 @@ describe('toFile', () => { expect(result.filename).toEqual('finetune.jsonl'); }); }); + +test('query strings', () => { + expect( + decodeURIComponent((client as any).stringifyQuery({ foo: { nested: { a: true, b: 'foo' } } })), + ).toEqual('foo[nested][a]=true&foo[nested][b]=foo'); + expect( + decodeURIComponent((client as any).stringifyQuery({ foo: { nested: { a: ['hello', 'world'] } } })), + ).toEqual('foo[nested][a][]=hello&foo[nested][a][]=world'); +}); diff --git a/ecosystem-tests/node-ts-esm-web/tests/test.ts b/ecosystem-tests/node-ts-esm-web/tests/test.ts index e0055c89f..675fb9a73 100644 --- a/ecosystem-tests/node-ts-esm-web/tests/test.ts +++ b/ecosystem-tests/node-ts-esm-web/tests/test.ts @@ -152,3 +152,12 @@ describe('toFile', () => { expect(result.filename).toEqual('finetune.jsonl'); }); }); + +test('query strings', () => { + expect( + decodeURIComponent((client as any).stringifyQuery({ foo: { nested: { a: true, b: 'foo' } } })), + ).toEqual('foo[nested][a]=true&foo[nested][b]=foo'); + expect( + decodeURIComponent((client as any).stringifyQuery({ foo: { nested: { a: ['hello', 'world'] } } })), + ).toEqual('foo[nested][a][]=hello&foo[nested][a][]=world'); +}); diff --git a/ecosystem-tests/node-ts-esm/tests/test-esnext.ts b/ecosystem-tests/node-ts-esm/tests/test-esnext.ts index d3b77971e..05cdd1047 100644 --- a/ecosystem-tests/node-ts-esm/tests/test-esnext.ts +++ b/ecosystem-tests/node-ts-esm/tests/test-esnext.ts @@ -64,3 +64,12 @@ it(`raw response`, async function () { const json: ChatCompletion = JSON.parse(chunks.join('')); expect(json.choices[0]?.message.content || '').toBeSimilarTo('This is a test', 10); }); + +test('query strings', () => { + expect( + decodeURIComponent((client as any).stringifyQuery({ foo: { nested: { a: true, b: 'foo' } } })), + ).toEqual('foo[nested][a]=true&foo[nested][b]=foo'); + expect( + decodeURIComponent((client as any).stringifyQuery({ foo: { nested: { a: ['hello', 'world'] } } })), + ).toEqual('foo[nested][a][]=hello&foo[nested][a][]=world'); +}); diff --git a/ecosystem-tests/node-ts-esm/tests/test.ts b/ecosystem-tests/node-ts-esm/tests/test.ts index 906220e95..7694a9874 100644 --- a/ecosystem-tests/node-ts-esm/tests/test.ts +++ b/ecosystem-tests/node-ts-esm/tests/test.ts @@ -173,3 +173,12 @@ describe('toFile', () => { expect(result.filename).toEqual('finetune.jsonl'); }); }); + +test('query strings', () => { + expect( + decodeURIComponent((client as any).stringifyQuery({ foo: { nested: { a: true, b: 'foo' } } })), + ).toEqual('foo[nested][a]=true&foo[nested][b]=foo'); + expect( + decodeURIComponent((client as any).stringifyQuery({ foo: { nested: { a: ['hello', 'world'] } } })), + ).toEqual('foo[nested][a][]=hello&foo[nested][a][]=world'); +}); diff --git a/ecosystem-tests/node-ts4.5-jest27/tests/test.ts b/ecosystem-tests/node-ts4.5-jest27/tests/test.ts index 5ece57019..3f6e5d572 100644 --- a/ecosystem-tests/node-ts4.5-jest27/tests/test.ts +++ b/ecosystem-tests/node-ts4.5-jest27/tests/test.ts @@ -192,3 +192,12 @@ describe('toFile', () => { expect(result.filename).toEqual('finetune.jsonl'); }); }); + +test('query strings', () => { + expect( + decodeURIComponent((client as any).stringifyQuery({ foo: { nested: { a: true, b: 'foo' } } })), + ).toEqual('foo[nested][a]=true&foo[nested][b]=foo'); + expect( + decodeURIComponent((client as any).stringifyQuery({ foo: { nested: { a: ['hello', 'world'] } } })), + ).toEqual('foo[nested][a][]=hello&foo[nested][a][]=world'); +}); diff --git a/ecosystem-tests/nodenext-tsup/index.ts b/ecosystem-tests/nodenext-tsup/index.ts new file mode 100644 index 000000000..f70568435 --- /dev/null +++ b/ecosystem-tests/nodenext-tsup/index.ts @@ -0,0 +1,41 @@ +import { OpenAI } from 'openai'; + +const openai = new OpenAI(); + +function assertEqual(actual: any, expected: any) { + if (actual === expected) { + return; + } + + console.error('expected', expected); + console.error('actual ', actual); + throw new Error('expected values to be equal'); +} + +async function main() { + const completion = await openai.chat.completions.create({ + model: 'gpt-4o-mini', + messages: [ + { + role: 'user', + content: 'What is the capital of the United States?', + }, + ], + }); + // smoke test for responses + if (!completion.choices[0]?.message.content) { + console.dir(completion, { depth: 4 }); + throw new Error('no response content!'); + } + + assertEqual( + decodeURIComponent((openai as any).stringifyQuery({ foo: { nested: { a: true, b: 'foo' } } })), + 'foo[nested][a]=true&foo[nested][b]=foo', + ); + assertEqual( + decodeURIComponent((openai as any).stringifyQuery({ foo: { nested: { a: ['hello', 'world'] } } })), + 'foo[nested][a][]=hello&foo[nested][a][]=world', + ); +} + +main(); diff --git a/ecosystem-tests/nodenext-tsup/package-lock.json b/ecosystem-tests/nodenext-tsup/package-lock.json new file mode 100644 index 000000000..8f4729374 --- /dev/null +++ b/ecosystem-tests/nodenext-tsup/package-lock.json @@ -0,0 +1,2078 @@ +{ + "name": "nodenext-tsup", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "nodenext-tsup", + "devDependencies": { + "tsup": "^8.2.4" + }, + "peerDependencies": { + "typescript": "^5.5.4" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz", + "integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz", + "integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz", + "integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz", + "integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz", + "integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz", + "integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz", + "integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz", + "integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz", + "integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz", + "integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz", + "integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz", + "integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz", + "integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz", + "integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz", + "integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz", + "integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz", + "integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz", + "integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz", + "integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz", + "integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz", + "integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz", + "integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz", + "integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz", + "integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.21.2.tgz", + "integrity": "sha512-fSuPrt0ZO8uXeS+xP3b+yYTCBUd05MoSp2N/MFOgjhhUhMmchXlpTQrTpI8T+YAwAQuK7MafsCOxW7VrPMrJcg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.21.2.tgz", + "integrity": "sha512-xGU5ZQmPlsjQS6tzTTGwMsnKUtu0WVbl0hYpTPauvbRAnmIvpInhJtgjj3mcuJpEiuUw4v1s4BimkdfDWlh7gA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.21.2.tgz", + "integrity": "sha512-99AhQ3/ZMxU7jw34Sq8brzXqWH/bMnf7ZVhvLk9QU2cOepbQSVTns6qoErJmSiAvU3InRqC2RRZ5ovh1KN0d0Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.21.2.tgz", + "integrity": "sha512-ZbRaUvw2iN/y37x6dY50D8m2BnDbBjlnMPotDi/qITMJ4sIxNY33HArjikDyakhSv0+ybdUxhWxE6kTI4oX26w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.21.2.tgz", + "integrity": "sha512-ztRJJMiE8nnU1YFcdbd9BcH6bGWG1z+jP+IPW2oDUAPxPjo9dverIOyXz76m6IPA6udEL12reYeLojzW2cYL7w==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.21.2.tgz", + "integrity": "sha512-flOcGHDZajGKYpLV0JNc0VFH361M7rnV1ee+NTeC/BQQ1/0pllYcFmxpagltANYt8FYf9+kL6RSk80Ziwyhr7w==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.21.2.tgz", + "integrity": "sha512-69CF19Kp3TdMopyteO/LJbWufOzqqXzkrv4L2sP8kfMaAQ6iwky7NoXTp7bD6/irKgknDKM0P9E/1l5XxVQAhw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.21.2.tgz", + "integrity": "sha512-48pD/fJkTiHAZTnZwR0VzHrao70/4MlzJrq0ZsILjLW/Ab/1XlVUStYyGt7tdyIiVSlGZbnliqmult/QGA2O2w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.21.2.tgz", + "integrity": "sha512-cZdyuInj0ofc7mAQpKcPR2a2iu4YM4FQfuUzCVA2u4HI95lCwzjoPtdWjdpDKyHxI0UO82bLDoOaLfpZ/wviyQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.21.2.tgz", + "integrity": "sha512-RL56JMT6NwQ0lXIQmMIWr1SW28z4E4pOhRRNqwWZeXpRlykRIlEpSWdsgNWJbYBEWD84eocjSGDu/XxbYeCmwg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.21.2.tgz", + "integrity": "sha512-PMxkrWS9z38bCr3rWvDFVGD6sFeZJw4iQlhrup7ReGmfn7Oukrr/zweLhYX6v2/8J6Cep9IEA/SmjXjCmSbrMQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.21.2.tgz", + "integrity": "sha512-B90tYAUoLhU22olrafY3JQCFLnT3NglazdwkHyxNDYF/zAxJt5fJUB/yBoWFoIQ7SQj+KLe3iL4BhOMa9fzgpw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.21.2.tgz", + "integrity": "sha512-7twFizNXudESmC9oneLGIUmoHiiLppz/Xs5uJQ4ShvE6234K0VB1/aJYU3f/4g7PhssLGKBVCC37uRkkOi8wjg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.21.2.tgz", + "integrity": "sha512-9rRero0E7qTeYf6+rFh3AErTNU1VCQg2mn7CQcI44vNUWM9Ze7MSRS/9RFuSsox+vstRt97+x3sOhEey024FRQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.21.2.tgz", + "integrity": "sha512-5rA4vjlqgrpbFVVHX3qkrCo/fZTj1q0Xxpg+Z7yIo3J2AilW7t2+n6Q8Jrx+4MrYpAnjttTYF8rr7bP46BPzRw==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.21.2.tgz", + "integrity": "sha512-6UUxd0+SKomjdzuAcp+HAmxw1FlGBnl1v2yEPSabtx4lBfdXHDVsW7+lQkgz9cNFJGY3AWR7+V8P5BqkD9L9nA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "dev": true + }, + "node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/bundle-require": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/bundle-require/-/bundle-require-5.0.0.tgz", + "integrity": "sha512-GuziW3fSSmopcx4KRymQEJVbZUfqlCqcq7dvs6TYwKRZiegK/2buMxQTPs6MGlNv50wms1699qYO54R8XfRX4w==", + "dev": true, + "dependencies": { + "load-tsconfig": "^0.2.3" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "peerDependencies": { + "esbuild": ">=0.18" + } + }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/consola": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.2.3.tgz", + "integrity": "sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ==", + "dev": true, + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", + "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/debug/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/esbuild": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", + "integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.23.1", + "@esbuild/android-arm": "0.23.1", + "@esbuild/android-arm64": "0.23.1", + "@esbuild/android-x64": "0.23.1", + "@esbuild/darwin-arm64": "0.23.1", + "@esbuild/darwin-x64": "0.23.1", + "@esbuild/freebsd-arm64": "0.23.1", + "@esbuild/freebsd-x64": "0.23.1", + "@esbuild/linux-arm": "0.23.1", + "@esbuild/linux-arm64": "0.23.1", + "@esbuild/linux-ia32": "0.23.1", + "@esbuild/linux-loong64": "0.23.1", + "@esbuild/linux-mips64el": "0.23.1", + "@esbuild/linux-ppc64": "0.23.1", + "@esbuild/linux-riscv64": "0.23.1", + "@esbuild/linux-s390x": "0.23.1", + "@esbuild/linux-x64": "0.23.1", + "@esbuild/netbsd-x64": "0.23.1", + "@esbuild/openbsd-arm64": "0.23.1", + "@esbuild/openbsd-x64": "0.23.1", + "@esbuild/sunos-x64": "0.23.1", + "@esbuild/win32-arm64": "0.23.1", + "@esbuild/win32-ia32": "0.23.1", + "@esbuild/win32-x64": "0.23.1" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/joycon": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", + "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/lilconfig": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", + "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/load-tsconfig": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/load-tsconfig/-/load-tsconfig-0.2.5.tgz", + "integrity": "sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==", + "dev": true + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", + "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==", + "dev": true + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", + "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/postcss-load-config": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", + "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "lilconfig": "^3.1.1" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "jiti": ">=1.21.0", + "postcss": ">=8.0.9", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.21.2.tgz", + "integrity": "sha512-e3TapAgYf9xjdLvKQCkQTnbTKd4a6jwlpQSJJFokHGaX2IVjoEqkIIhiQfqsi0cdwlOD+tQGuOd5AJkc5RngBw==", + "dev": true, + "dependencies": { + "@types/estree": "1.0.5" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.21.2", + "@rollup/rollup-android-arm64": "4.21.2", + "@rollup/rollup-darwin-arm64": "4.21.2", + "@rollup/rollup-darwin-x64": "4.21.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.21.2", + "@rollup/rollup-linux-arm-musleabihf": "4.21.2", + "@rollup/rollup-linux-arm64-gnu": "4.21.2", + "@rollup/rollup-linux-arm64-musl": "4.21.2", + "@rollup/rollup-linux-powerpc64le-gnu": "4.21.2", + "@rollup/rollup-linux-riscv64-gnu": "4.21.2", + "@rollup/rollup-linux-s390x-gnu": "4.21.2", + "@rollup/rollup-linux-x64-gnu": "4.21.2", + "@rollup/rollup-linux-x64-musl": "4.21.2", + "@rollup/rollup-win32-arm64-msvc": "4.21.2", + "@rollup/rollup-win32-ia32-msvc": "4.21.2", + "@rollup/rollup-win32-x64-msvc": "4.21.2", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.8.0-beta.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz", + "integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==", + "dev": true, + "dependencies": { + "whatwg-url": "^7.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/source-map/node_modules/tr46": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", + "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/source-map/node_modules/webidl-conversions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", + "dev": true + }, + "node_modules/source-map/node_modules/whatwg-url": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", + "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", + "dev": true, + "dependencies": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/sucrase": { + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", + "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "^10.3.10", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true + }, + "node_modules/tsup": { + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/tsup/-/tsup-8.2.4.tgz", + "integrity": "sha512-akpCPePnBnC/CXgRrcy72ZSntgIEUa1jN0oJbbvpALWKNOz1B7aM+UVDWGRGIO/T/PZugAESWDJUAb5FD48o8Q==", + "dev": true, + "dependencies": { + "bundle-require": "^5.0.0", + "cac": "^6.7.14", + "chokidar": "^3.6.0", + "consola": "^3.2.3", + "debug": "^4.3.5", + "esbuild": "^0.23.0", + "execa": "^5.1.1", + "globby": "^11.1.0", + "joycon": "^3.1.1", + "picocolors": "^1.0.1", + "postcss-load-config": "^6.0.1", + "resolve-from": "^5.0.0", + "rollup": "^4.19.0", + "source-map": "0.8.0-beta.0", + "sucrase": "^3.35.0", + "tree-kill": "^1.2.2" + }, + "bin": { + "tsup": "dist/cli-default.js", + "tsup-node": "dist/cli-node.js" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@microsoft/api-extractor": "^7.36.0", + "@swc/core": "^1", + "postcss": "^8.4.12", + "typescript": ">=4.5.0" + }, + "peerDependenciesMeta": { + "@microsoft/api-extractor": { + "optional": true + }, + "@swc/core": { + "optional": true + }, + "postcss": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/typescript": { + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", + "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + } + } +} diff --git a/ecosystem-tests/nodenext-tsup/package.json b/ecosystem-tests/nodenext-tsup/package.json new file mode 100644 index 000000000..ddef80219 --- /dev/null +++ b/ecosystem-tests/nodenext-tsup/package.json @@ -0,0 +1,16 @@ +{ + "name": "nodenext-tsup", + "module": "index.ts", + "type": "module", + "scripts": { + "build": "tsup", + "main": "npm run build && node dist/index.cjs" + }, + "dependencies": {}, + "devDependencies": { + "tsup": "^8.2.4" + }, + "peerDependencies": { + "typescript": "^5.5.4" + } +} diff --git a/ecosystem-tests/nodenext-tsup/tsconfig.json b/ecosystem-tests/nodenext-tsup/tsconfig.json new file mode 100644 index 000000000..49111f4a1 --- /dev/null +++ b/ecosystem-tests/nodenext-tsup/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "declaration": true, + "declarationMap": true, + "esModuleInterop": true, + "incremental": false, + "isolatedModules": true, + "lib": ["es2022", "DOM", "DOM.Iterable"], + "module": "NodeNext", + "moduleDetection": "force", + "moduleResolution": "NodeNext", + "noUncheckedIndexedAccess": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "strict": true, + "target": "ES2022" + } +} diff --git a/ecosystem-tests/nodenext-tsup/tsup.config.ts b/ecosystem-tests/nodenext-tsup/tsup.config.ts new file mode 100644 index 000000000..657cd79fa --- /dev/null +++ b/ecosystem-tests/nodenext-tsup/tsup.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from "tsup"; + +export default defineConfig({ + entry: ["index.ts"], + noExternal: ["openai"], + platform: "neutral", +}); diff --git a/ecosystem-tests/ts-browser-webpack/src/index.ts b/ecosystem-tests/ts-browser-webpack/src/index.ts index b7821f568..75fb6ea7a 100644 --- a/ecosystem-tests/ts-browser-webpack/src/index.ts +++ b/ecosystem-tests/ts-browser-webpack/src/index.ts @@ -209,4 +209,13 @@ describe('toFile', () => { }); }); +it('handles query strings', () => { + expect( + decodeURIComponent((client as any).stringifyQuery({ foo: { nested: { a: true, b: 'foo' } } })), + ).toEqual('foo[nested][a]=true&foo[nested][b]=foo'); + expect( + decodeURIComponent((client as any).stringifyQuery({ foo: { nested: { a: ['hello', 'world'] } } })), + ).toEqual('foo[nested][a][]=hello&foo[nested][a][]=world'); +}); + runTests(); diff --git a/ecosystem-tests/vercel-edge/src/uploadWebApiTestCases.ts b/ecosystem-tests/vercel-edge/src/uploadWebApiTestCases.ts index 3f2c6b468..eb8be0030 100644 --- a/ecosystem-tests/vercel-edge/src/uploadWebApiTestCases.ts +++ b/ecosystem-tests/vercel-edge/src/uploadWebApiTestCases.ts @@ -180,4 +180,15 @@ export function uploadWebApiTestCases({ expectEqual(result.filename, 'finetune.jsonl'); }); } + + it('handles query strings', async () => { + expectEqual( + decodeURIComponent((client as any).stringifyQuery({ foo: { nested: { a: true, b: 'foo' } } })), + 'foo[nested][a]=true&foo[nested][b]=foo', + ); + expectEqual( + decodeURIComponent((client as any).stringifyQuery({ foo: { nested: { a: ['hello', 'world'] } } })), + 'foo[nested][a][]=hello&foo[nested][a][]=world', + ); + }); } diff --git a/package.json b/package.json index 86e594c2c..69dab3ea5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "openai", - "version": "4.61.1", + "version": "4.62.0", "description": "The official TypeScript library for the OpenAI API", "author": "OpenAI ", "types": "dist/index.d.ts", @@ -26,13 +26,11 @@ "dependencies": { "@types/node": "^18.11.18", "@types/node-fetch": "^2.6.4", - "@types/qs": "^6.9.15", "abort-controller": "^3.0.0", "agentkeepalive": "^4.2.1", "form-data-encoder": "1.7.2", "formdata-node": "^4.3.2", - "node-fetch": "^2.6.7", - "qs": "^6.10.3" + "node-fetch": "^2.6.7" }, "devDependencies": { "@swc/core": "^1.3.102", @@ -43,6 +41,7 @@ "eslint": "^8.49.0", "eslint-plugin-prettier": "^5.0.1", "eslint-plugin-unused-imports": "^3.0.0", + "iconv-lite": "^0.6.3", "fast-check": "^3.22.0", "jest": "^29.4.0", "prettier": "^3.0.0", diff --git a/scripts/build-deno b/scripts/build-deno index 641b61c02..e773b7fc5 100755 --- a/scripts/build-deno +++ b/scripts/build-deno @@ -16,7 +16,7 @@ This is a build produced from https://github.com/openai/openai-node – please g Usage: \`\`\`ts -import OpenAI from "https://deno.land/x/openai@v4.61.1/mod.ts"; +import OpenAI from "https://deno.land/x/openai@v4.62.0/mod.ts"; const client = new OpenAI(); \`\`\` diff --git a/scripts/utils/denoify.ts b/scripts/utils/denoify.ts index 742bc069f..52705802a 100644 --- a/scripts/utils/denoify.ts +++ b/scripts/utils/denoify.ts @@ -102,9 +102,6 @@ async function denoify() { } else if (specifier.startsWith(pkgName + '/')) { // convert self-referencing module specifiers to relative paths specifier = file.getRelativePathAsModuleSpecifierTo(denoDir + specifier.substring(pkgName.length)); - } else if (specifier === 'qs') { - decl.replaceWithText(`import { qs } from "https://deno.land/x/deno_qs@0.0.1/mod.ts"`); - continue; } else if (!decl.isModuleSpecifierRelative()) { specifier = `npm:${specifier}`; } diff --git a/src/core.ts b/src/core.ts index a4bb87a32..90714d3ce 100644 --- a/src/core.ts +++ b/src/core.ts @@ -37,7 +37,7 @@ type APIResponseProps = { controller: AbortController; }; -async function defaultParseResponse(props: APIResponseProps): Promise { +async function defaultParseResponse(props: APIResponseProps): Promise> { const { response } = props; if (props.options.stream) { debug('response', response.status, response.url, response.headers, response.body); @@ -54,11 +54,11 @@ async function defaultParseResponse(props: APIResponseProps): Promise { // fetch refuses to read the body when the status code is 204. if (response.status === 204) { - return null as T; + return null as WithRequestID; } if (props.options.__binaryResponse) { - return response as unknown as T; + return response as unknown as WithRequestID; } const contentType = response.headers.get('content-type'); @@ -69,26 +69,44 @@ async function defaultParseResponse(props: APIResponseProps): Promise { debug('response', response.status, response.url, response.headers, json); - return json as T; + return _addRequestID(json, response); } const text = await response.text(); debug('response', response.status, response.url, response.headers, text); // TODO handle blob, arraybuffer, other content types, etc. - return text as unknown as T; + return text as unknown as WithRequestID; +} + +type WithRequestID = + T extends Array | Response | AbstractPage ? T + : T extends Record ? T & { _request_id?: string | null } + : T; + +function _addRequestID(value: T, response: Response): WithRequestID { + if (!value || typeof value !== 'object' || Array.isArray(value)) { + return value as WithRequestID; + } + + return Object.defineProperty(value, '_request_id', { + value: response.headers.get('x-request-id'), + enumerable: false, + }) as WithRequestID; } /** * A subclass of `Promise` providing additional helper methods * for interacting with the SDK. */ -export class APIPromise extends Promise { - private parsedPromise: Promise | undefined; +export class APIPromise extends Promise> { + private parsedPromise: Promise> | undefined; constructor( private responsePromise: Promise, - private parseResponse: (props: APIResponseProps) => PromiseOrValue = defaultParseResponse, + private parseResponse: ( + props: APIResponseProps, + ) => PromiseOrValue> = defaultParseResponse, ) { super((resolve) => { // this is maybe a bit weird but this has to be a no-op to not implicitly @@ -99,7 +117,9 @@ export class APIPromise extends Promise { } _thenUnwrap(transform: (data: T) => U): APIPromise { - return new APIPromise(this.responsePromise, async (props) => transform(await this.parseResponse(props))); + return new APIPromise(this.responsePromise, async (props) => + _addRequestID(transform(await this.parseResponse(props)), props.response), + ); } /** @@ -136,15 +156,15 @@ export class APIPromise extends Promise { return { data, response }; } - private parse(): Promise { + private parse(): Promise> { if (!this.parsedPromise) { - this.parsedPromise = this.responsePromise.then(this.parseResponse); + this.parsedPromise = this.responsePromise.then(this.parseResponse) as any as Promise>; } return this.parsedPromise; } - override then( - onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, + override then, TResult2 = never>( + onfulfilled?: ((value: WithRequestID) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null, ): Promise { return this.parse().then(onfulfilled, onrejected); @@ -152,11 +172,11 @@ export class APIPromise extends Promise { override catch( onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null, - ): Promise { + ): Promise | TResult> { return this.parse().catch(onrejected); } - override finally(onfinally?: (() => void) | undefined | null): Promise { + override finally(onfinally?: (() => void) | undefined | null): Promise> { return this.parse().finally(onfinally); } } @@ -706,7 +726,13 @@ export class PagePromise< ) { super( request, - async (props) => new Page(client, props.response, await defaultParseResponse(props), props.options), + async (props) => + new Page( + client, + props.response, + await defaultParseResponse(props), + props.options, + ) as WithRequestID, ); } diff --git a/src/index.ts b/src/index.ts index 36064286d..b52406f6c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,10 +2,8 @@ import * as Errors from './error'; import * as Uploads from './uploads'; - import { type Agent, type RequestInit } from './_shims/index'; -import * as qs from 'qs'; - +import * as qs from './internal/qs'; import * as Core from './core'; import * as Pagination from './pagination'; import * as API from './resources/index'; diff --git a/src/internal/qs/LICENSE.md b/src/internal/qs/LICENSE.md new file mode 100644 index 000000000..3fda1573b --- /dev/null +++ b/src/internal/qs/LICENSE.md @@ -0,0 +1,13 @@ +BSD 3-Clause License + +Copyright (c) 2014, Nathan LaFreniere and other [contributors](https://github.com/puruvj/neoqs/graphs/contributors) All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/internal/qs/README.md b/src/internal/qs/README.md new file mode 100644 index 000000000..67ae04ecd --- /dev/null +++ b/src/internal/qs/README.md @@ -0,0 +1,3 @@ +# qs + +This is a vendored version of [neoqs](https://github.com/PuruVJ/neoqs) which is a TypeScript rewrite of [qs](https://github.com/ljharb/qs), a query string library. diff --git a/src/internal/qs/formats.ts b/src/internal/qs/formats.ts new file mode 100644 index 000000000..1cf9e2cde --- /dev/null +++ b/src/internal/qs/formats.ts @@ -0,0 +1,9 @@ +import type { Format } from './types'; + +export const default_format: Format = 'RFC3986'; +export const formatters: Record string> = { + RFC1738: (v: PropertyKey) => String(v).replace(/%20/g, '+'), + RFC3986: (v: PropertyKey) => String(v), +}; +export const RFC1738 = 'RFC1738'; +export const RFC3986 = 'RFC3986'; diff --git a/src/internal/qs/index.ts b/src/internal/qs/index.ts new file mode 100644 index 000000000..c3a3620d0 --- /dev/null +++ b/src/internal/qs/index.ts @@ -0,0 +1,13 @@ +import { default_format, formatters, RFC1738, RFC3986 } from './formats'; + +const formats = { + formatters, + RFC1738, + RFC3986, + default: default_format, +}; + +export { stringify } from './stringify'; +export { formats }; + +export type { DefaultDecoder, DefaultEncoder, Format, ParseOptions, StringifyOptions } from './types'; diff --git a/src/internal/qs/stringify.ts b/src/internal/qs/stringify.ts new file mode 100644 index 000000000..67497561a --- /dev/null +++ b/src/internal/qs/stringify.ts @@ -0,0 +1,388 @@ +import { encode, is_buffer, maybe_map } from './utils'; +import { default_format, formatters } from './formats'; +import type { NonNullableProperties, StringifyOptions } from './types'; + +const has = Object.prototype.hasOwnProperty; + +const array_prefix_generators = { + brackets(prefix: PropertyKey) { + return String(prefix) + '[]'; + }, + comma: 'comma', + indices(prefix: PropertyKey, key: string) { + return String(prefix) + '[' + key + ']'; + }, + repeat(prefix: PropertyKey) { + return String(prefix); + }, +}; + +const is_array = Array.isArray; +const push = Array.prototype.push; +const push_to_array = function (arr: any[], value_or_array: any) { + push.apply(arr, is_array(value_or_array) ? value_or_array : [value_or_array]); +}; + +const to_ISO = Date.prototype.toISOString; + +const defaults = { + addQueryPrefix: false, + allowDots: false, + allowEmptyArrays: false, + arrayFormat: 'indices', + charset: 'utf-8', + charsetSentinel: false, + delimiter: '&', + encode: true, + encodeDotInKeys: false, + encoder: encode, + encodeValuesOnly: false, + format: default_format, + formatter: formatters[default_format], + /** @deprecated */ + indices: false, + serializeDate(date) { + return to_ISO.call(date); + }, + skipNulls: false, + strictNullHandling: false, +} as NonNullableProperties; + +function is_non_nullish_primitive(v: unknown): v is string | number | boolean | symbol | bigint { + return ( + typeof v === 'string' || + typeof v === 'number' || + typeof v === 'boolean' || + typeof v === 'symbol' || + typeof v === 'bigint' + ); +} + +const sentinel = {}; + +function inner_stringify( + object: any, + prefix: PropertyKey, + generateArrayPrefix: StringifyOptions['arrayFormat'] | ((prefix: string, key: string) => string), + commaRoundTrip: boolean, + allowEmptyArrays: boolean, + strictNullHandling: boolean, + skipNulls: boolean, + encodeDotInKeys: boolean, + encoder: StringifyOptions['encoder'], + filter: StringifyOptions['filter'], + sort: StringifyOptions['sort'], + allowDots: StringifyOptions['allowDots'], + serializeDate: StringifyOptions['serializeDate'], + format: StringifyOptions['format'], + formatter: StringifyOptions['formatter'], + encodeValuesOnly: boolean, + charset: StringifyOptions['charset'], + sideChannel: WeakMap, +) { + let obj = object; + + let tmp_sc = sideChannel; + let step = 0; + let find_flag = false; + while ((tmp_sc = tmp_sc.get(sentinel)) !== void undefined && !find_flag) { + // Where object last appeared in the ref tree + const pos = tmp_sc.get(object); + step += 1; + if (typeof pos !== 'undefined') { + if (pos === step) { + throw new RangeError('Cyclic object value'); + } else { + find_flag = true; // Break while + } + } + if (typeof tmp_sc.get(sentinel) === 'undefined') { + step = 0; + } + } + + if (typeof filter === 'function') { + obj = filter(prefix, obj); + } else if (obj instanceof Date) { + obj = serializeDate?.(obj); + } else if (generateArrayPrefix === 'comma' && is_array(obj)) { + obj = maybe_map(obj, function (value) { + if (value instanceof Date) { + return serializeDate?.(value); + } + return value; + }); + } + + if (obj === null) { + if (strictNullHandling) { + return encoder && !encodeValuesOnly ? + // @ts-expect-error + encoder(prefix, defaults.encoder, charset, 'key', format) + : prefix; + } + + obj = ''; + } + + if (is_non_nullish_primitive(obj) || is_buffer(obj)) { + if (encoder) { + const key_value = + encodeValuesOnly ? prefix + // @ts-expect-error + : encoder(prefix, defaults.encoder, charset, 'key', format); + return [ + formatter?.(key_value) + + '=' + + // @ts-expect-error + formatter?.(encoder(obj, defaults.encoder, charset, 'value', format)), + ]; + } + return [formatter?.(prefix) + '=' + formatter?.(String(obj))]; + } + + const values: string[] = []; + + if (typeof obj === 'undefined') { + return values; + } + + let obj_keys; + if (generateArrayPrefix === 'comma' && is_array(obj)) { + // we need to join elements in + if (encodeValuesOnly && encoder) { + // @ts-expect-error values only + obj = maybe_map(obj, encoder); + } + obj_keys = [{ value: obj.length > 0 ? obj.join(',') || null : void undefined }]; + } else if (is_array(filter)) { + obj_keys = filter; + } else { + const keys = Object.keys(obj); + obj_keys = sort ? keys.sort(sort) : keys; + } + + const encoded_prefix = encodeDotInKeys ? String(prefix).replace(/\./g, '%2E') : String(prefix); + + const adjusted_prefix = + commaRoundTrip && is_array(obj) && obj.length === 1 ? encoded_prefix + '[]' : encoded_prefix; + + if (allowEmptyArrays && is_array(obj) && obj.length === 0) { + return adjusted_prefix + '[]'; + } + + for (let j = 0; j < obj_keys.length; ++j) { + const key = obj_keys[j]; + const value = + // @ts-ignore + typeof key === 'object' && typeof key.value !== 'undefined' ? key.value : obj[key as any]; + + if (skipNulls && value === null) { + continue; + } + + // @ts-ignore + const encoded_key = allowDots && encodeDotInKeys ? (key as any).replace(/\./g, '%2E') : key; + const key_prefix = + is_array(obj) ? + typeof generateArrayPrefix === 'function' ? + generateArrayPrefix(adjusted_prefix, encoded_key) + : adjusted_prefix + : adjusted_prefix + (allowDots ? '.' + encoded_key : '[' + encoded_key + ']'); + + sideChannel.set(object, step); + const valueSideChannel = new WeakMap(); + valueSideChannel.set(sentinel, sideChannel); + push_to_array( + values, + inner_stringify( + value, + key_prefix, + generateArrayPrefix, + commaRoundTrip, + allowEmptyArrays, + strictNullHandling, + skipNulls, + encodeDotInKeys, + // @ts-ignore + generateArrayPrefix === 'comma' && encodeValuesOnly && is_array(obj) ? null : encoder, + filter, + sort, + allowDots, + serializeDate, + format, + formatter, + encodeValuesOnly, + charset, + valueSideChannel, + ), + ); + } + + return values; +} + +function normalize_stringify_options( + opts: StringifyOptions = defaults, +): NonNullableProperties> & { indices?: boolean } { + if (typeof opts.allowEmptyArrays !== 'undefined' && typeof opts.allowEmptyArrays !== 'boolean') { + throw new TypeError('`allowEmptyArrays` option can only be `true` or `false`, when provided'); + } + + if (typeof opts.encodeDotInKeys !== 'undefined' && typeof opts.encodeDotInKeys !== 'boolean') { + throw new TypeError('`encodeDotInKeys` option can only be `true` or `false`, when provided'); + } + + if (opts.encoder !== null && typeof opts.encoder !== 'undefined' && typeof opts.encoder !== 'function') { + throw new TypeError('Encoder has to be a function.'); + } + + const charset = opts.charset || defaults.charset; + if (typeof opts.charset !== 'undefined' && opts.charset !== 'utf-8' && opts.charset !== 'iso-8859-1') { + throw new TypeError('The charset option must be either utf-8, iso-8859-1, or undefined'); + } + + let format = default_format; + if (typeof opts.format !== 'undefined') { + if (!has.call(formatters, opts.format)) { + throw new TypeError('Unknown format option provided.'); + } + format = opts.format; + } + const formatter = formatters[format]; + + let filter = defaults.filter; + if (typeof opts.filter === 'function' || is_array(opts.filter)) { + filter = opts.filter; + } + + let arrayFormat: StringifyOptions['arrayFormat']; + if (opts.arrayFormat && opts.arrayFormat in array_prefix_generators) { + arrayFormat = opts.arrayFormat; + } else if ('indices' in opts) { + arrayFormat = opts.indices ? 'indices' : 'repeat'; + } else { + arrayFormat = defaults.arrayFormat; + } + + if ('commaRoundTrip' in opts && typeof opts.commaRoundTrip !== 'boolean') { + throw new TypeError('`commaRoundTrip` must be a boolean, or absent'); + } + + const allowDots = + typeof opts.allowDots === 'undefined' ? + !!opts.encodeDotInKeys === true ? + true + : defaults.allowDots + : !!opts.allowDots; + + return { + addQueryPrefix: typeof opts.addQueryPrefix === 'boolean' ? opts.addQueryPrefix : defaults.addQueryPrefix, + // @ts-ignore + allowDots: allowDots, + allowEmptyArrays: + typeof opts.allowEmptyArrays === 'boolean' ? !!opts.allowEmptyArrays : defaults.allowEmptyArrays, + arrayFormat: arrayFormat, + charset: charset, + charsetSentinel: + typeof opts.charsetSentinel === 'boolean' ? opts.charsetSentinel : defaults.charsetSentinel, + commaRoundTrip: !!opts.commaRoundTrip, + delimiter: typeof opts.delimiter === 'undefined' ? defaults.delimiter : opts.delimiter, + encode: typeof opts.encode === 'boolean' ? opts.encode : defaults.encode, + encodeDotInKeys: + typeof opts.encodeDotInKeys === 'boolean' ? opts.encodeDotInKeys : defaults.encodeDotInKeys, + encoder: typeof opts.encoder === 'function' ? opts.encoder : defaults.encoder, + encodeValuesOnly: + typeof opts.encodeValuesOnly === 'boolean' ? opts.encodeValuesOnly : defaults.encodeValuesOnly, + filter: filter, + format: format, + formatter: formatter, + serializeDate: typeof opts.serializeDate === 'function' ? opts.serializeDate : defaults.serializeDate, + skipNulls: typeof opts.skipNulls === 'boolean' ? opts.skipNulls : defaults.skipNulls, + // @ts-ignore + sort: typeof opts.sort === 'function' ? opts.sort : null, + strictNullHandling: + typeof opts.strictNullHandling === 'boolean' ? opts.strictNullHandling : defaults.strictNullHandling, + }; +} + +export function stringify(object: any, opts: StringifyOptions = {}) { + let obj = object; + const options = normalize_stringify_options(opts); + + let obj_keys: PropertyKey[] | undefined; + let filter; + + if (typeof options.filter === 'function') { + filter = options.filter; + obj = filter('', obj); + } else if (is_array(options.filter)) { + filter = options.filter; + obj_keys = filter; + } + + const keys: string[] = []; + + if (typeof obj !== 'object' || obj === null) { + return ''; + } + + const generateArrayPrefix = array_prefix_generators[options.arrayFormat]; + const commaRoundTrip = generateArrayPrefix === 'comma' && options.commaRoundTrip; + + if (!obj_keys) { + obj_keys = Object.keys(obj); + } + + if (options.sort) { + obj_keys.sort(options.sort); + } + + const sideChannel = new WeakMap(); + for (let i = 0; i < obj_keys.length; ++i) { + const key = obj_keys[i]!; + + if (options.skipNulls && obj[key] === null) { + continue; + } + push_to_array( + keys, + inner_stringify( + obj[key], + key, + // @ts-expect-error + generateArrayPrefix, + commaRoundTrip, + options.allowEmptyArrays, + options.strictNullHandling, + options.skipNulls, + options.encodeDotInKeys, + options.encode ? options.encoder : null, + options.filter, + options.sort, + options.allowDots, + options.serializeDate, + options.format, + options.formatter, + options.encodeValuesOnly, + options.charset, + sideChannel, + ), + ); + } + + const joined = keys.join(options.delimiter); + let prefix = options.addQueryPrefix === true ? '?' : ''; + + if (options.charsetSentinel) { + if (options.charset === 'iso-8859-1') { + // encodeURIComponent('✓'), the "numeric entity" representation of a checkmark + prefix += 'utf8=%26%2310003%3B&'; + } else { + // encodeURIComponent('✓') + prefix += 'utf8=%E2%9C%93&'; + } + } + + return joined.length > 0 ? prefix + joined : ''; +} diff --git a/src/internal/qs/types.ts b/src/internal/qs/types.ts new file mode 100644 index 000000000..7c28dbb46 --- /dev/null +++ b/src/internal/qs/types.ts @@ -0,0 +1,71 @@ +export type Format = 'RFC1738' | 'RFC3986'; + +export type DefaultEncoder = (str: any, defaultEncoder?: any, charset?: string) => string; +export type DefaultDecoder = (str: string, decoder?: any, charset?: string) => string; + +export type BooleanOptional = boolean | undefined; + +export type StringifyBaseOptions = { + delimiter?: string; + allowDots?: boolean; + encodeDotInKeys?: boolean; + strictNullHandling?: boolean; + skipNulls?: boolean; + encode?: boolean; + encoder?: ( + str: any, + defaultEncoder: DefaultEncoder, + charset: string, + type: 'key' | 'value', + format?: Format, + ) => string; + filter?: Array | ((prefix: PropertyKey, value: any) => any); + arrayFormat?: 'indices' | 'brackets' | 'repeat' | 'comma'; + indices?: boolean; + sort?: ((a: PropertyKey, b: PropertyKey) => number) | null; + serializeDate?: (d: Date) => string; + format?: 'RFC1738' | 'RFC3986'; + formatter?: (str: PropertyKey) => string; + encodeValuesOnly?: boolean; + addQueryPrefix?: boolean; + charset?: 'utf-8' | 'iso-8859-1'; + charsetSentinel?: boolean; + allowEmptyArrays?: boolean; + commaRoundTrip?: boolean; +}; + +export type StringifyOptions = StringifyBaseOptions; + +export type ParseBaseOptions = { + comma?: boolean; + delimiter?: string | RegExp; + depth?: number | false; + decoder?: (str: string, defaultDecoder: DefaultDecoder, charset: string, type: 'key' | 'value') => any; + arrayLimit?: number; + parseArrays?: boolean; + plainObjects?: boolean; + allowPrototypes?: boolean; + allowSparse?: boolean; + parameterLimit?: number; + strictDepth?: boolean; + strictNullHandling?: boolean; + ignoreQueryPrefix?: boolean; + charset?: 'utf-8' | 'iso-8859-1'; + charsetSentinel?: boolean; + interpretNumericEntities?: boolean; + allowEmptyArrays?: boolean; + duplicates?: 'combine' | 'first' | 'last'; + allowDots?: boolean; + decodeDotInKeys?: boolean; +}; + +export type ParseOptions = ParseBaseOptions; + +export type ParsedQs = { + [key: string]: undefined | string | string[] | ParsedQs | ParsedQs[]; +}; + +// Type to remove null or undefined union from each property +export type NonNullableProperties = { + [K in keyof T]-?: Exclude; +}; diff --git a/src/internal/qs/utils.ts b/src/internal/qs/utils.ts new file mode 100644 index 000000000..113b18fb9 --- /dev/null +++ b/src/internal/qs/utils.ts @@ -0,0 +1,265 @@ +import { RFC1738 } from './formats'; +import type { DefaultEncoder, Format } from './types'; + +const has = Object.prototype.hasOwnProperty; +const is_array = Array.isArray; + +const hex_table = (() => { + const array = []; + for (let i = 0; i < 256; ++i) { + array.push('%' + ((i < 16 ? '0' : '') + i.toString(16)).toUpperCase()); + } + + return array; +})(); + +function compact_queue>(queue: Array<{ obj: T; prop: string }>) { + while (queue.length > 1) { + const item = queue.pop(); + if (!item) continue; + + const obj = item.obj[item.prop]; + + if (is_array(obj)) { + const compacted: unknown[] = []; + + for (let j = 0; j < obj.length; ++j) { + if (typeof obj[j] !== 'undefined') { + compacted.push(obj[j]); + } + } + + // @ts-ignore + item.obj[item.prop] = compacted; + } + } +} + +function array_to_object(source: any[], options: { plainObjects: boolean }) { + const obj = options && options.plainObjects ? Object.create(null) : {}; + for (let i = 0; i < source.length; ++i) { + if (typeof source[i] !== 'undefined') { + obj[i] = source[i]; + } + } + + return obj; +} + +export function merge( + target: any, + source: any, + options: { plainObjects?: boolean; allowPrototypes?: boolean } = {}, +) { + if (!source) { + return target; + } + + if (typeof source !== 'object') { + if (is_array(target)) { + target.push(source); + } else if (target && typeof target === 'object') { + if ( + (options && (options.plainObjects || options.allowPrototypes)) || + !has.call(Object.prototype, source) + ) { + target[source] = true; + } + } else { + return [target, source]; + } + + return target; + } + + if (!target || typeof target !== 'object') { + return [target].concat(source); + } + + let mergeTarget = target; + if (is_array(target) && !is_array(source)) { + // @ts-ignore + mergeTarget = array_to_object(target, options); + } + + if (is_array(target) && is_array(source)) { + source.forEach(function (item, i) { + if (has.call(target, i)) { + const targetItem = target[i]; + if (targetItem && typeof targetItem === 'object' && item && typeof item === 'object') { + target[i] = merge(targetItem, item, options); + } else { + target.push(item); + } + } else { + target[i] = item; + } + }); + return target; + } + + return Object.keys(source).reduce(function (acc, key) { + const value = source[key]; + + if (has.call(acc, key)) { + acc[key] = merge(acc[key], value, options); + } else { + acc[key] = value; + } + return acc; + }, mergeTarget); +} + +export function assign_single_source(target: any, source: any) { + return Object.keys(source).reduce(function (acc, key) { + acc[key] = source[key]; + return acc; + }, target); +} + +export function decode(str: string, _: any, charset: string) { + const strWithoutPlus = str.replace(/\+/g, ' '); + if (charset === 'iso-8859-1') { + // unescape never throws, no try...catch needed: + return strWithoutPlus.replace(/%[0-9a-f]{2}/gi, unescape); + } + // utf-8 + try { + return decodeURIComponent(strWithoutPlus); + } catch (e) { + return strWithoutPlus; + } +} + +const limit = 1024; + +export const encode: ( + str: any, + defaultEncoder: DefaultEncoder, + charset: string, + type: 'key' | 'value', + format: Format, +) => string = (str, _defaultEncoder, charset, _kind, format: Format) => { + // This code was originally written by Brian White for the io.js core querystring library. + // It has been adapted here for stricter adherence to RFC 3986 + if (str.length === 0) { + return str; + } + + let string = str; + if (typeof str === 'symbol') { + string = Symbol.prototype.toString.call(str); + } else if (typeof str !== 'string') { + string = String(str); + } + + if (charset === 'iso-8859-1') { + return escape(string).replace(/%u[0-9a-f]{4}/gi, function ($0) { + return '%26%23' + parseInt($0.slice(2), 16) + '%3B'; + }); + } + + let out = ''; + for (let j = 0; j < string.length; j += limit) { + const segment = string.length >= limit ? string.slice(j, j + limit) : string; + const arr = []; + + for (let i = 0; i < segment.length; ++i) { + let c = segment.charCodeAt(i); + if ( + c === 0x2d || // - + c === 0x2e || // . + c === 0x5f || // _ + c === 0x7e || // ~ + (c >= 0x30 && c <= 0x39) || // 0-9 + (c >= 0x41 && c <= 0x5a) || // a-z + (c >= 0x61 && c <= 0x7a) || // A-Z + (format === RFC1738 && (c === 0x28 || c === 0x29)) // ( ) + ) { + arr[arr.length] = segment.charAt(i); + continue; + } + + if (c < 0x80) { + arr[arr.length] = hex_table[c]; + continue; + } + + if (c < 0x800) { + arr[arr.length] = hex_table[0xc0 | (c >> 6)]! + hex_table[0x80 | (c & 0x3f)]; + continue; + } + + if (c < 0xd800 || c >= 0xe000) { + arr[arr.length] = + hex_table[0xe0 | (c >> 12)]! + hex_table[0x80 | ((c >> 6) & 0x3f)] + hex_table[0x80 | (c & 0x3f)]; + continue; + } + + i += 1; + c = 0x10000 + (((c & 0x3ff) << 10) | (segment.charCodeAt(i) & 0x3ff)); + + arr[arr.length] = + hex_table[0xf0 | (c >> 18)]! + + hex_table[0x80 | ((c >> 12) & 0x3f)] + + hex_table[0x80 | ((c >> 6) & 0x3f)] + + hex_table[0x80 | (c & 0x3f)]; + } + + out += arr.join(''); + } + + return out; +}; + +export function compact(value: any) { + const queue = [{ obj: { o: value }, prop: 'o' }]; + const refs = []; + + for (let i = 0; i < queue.length; ++i) { + const item = queue[i]; + // @ts-ignore + const obj = item.obj[item.prop]; + + const keys = Object.keys(obj); + for (let j = 0; j < keys.length; ++j) { + const key = keys[j]!; + const val = obj[key]; + if (typeof val === 'object' && val !== null && refs.indexOf(val) === -1) { + queue.push({ obj: obj, prop: key }); + refs.push(val); + } + } + } + + compact_queue(queue); + + return value; +} + +export function is_regexp(obj: any) { + return Object.prototype.toString.call(obj) === '[object RegExp]'; +} + +export function is_buffer(obj: any) { + if (!obj || typeof obj !== 'object') { + return false; + } + + return !!(obj.constructor && obj.constructor.isBuffer && obj.constructor.isBuffer(obj)); +} + +export function combine(a: any, b: any) { + return [].concat(a, b); +} + +export function maybe_map(val: T[], fn: (v: T) => T) { + if (is_array(val)) { + const mapped = []; + for (let i = 0; i < val.length; i += 1) { + mapped.push(fn(val[i]!)); + } + return mapped; + } + return fn(val); +} diff --git a/src/version.ts b/src/version.ts index 6c1e0cb8e..70b394b16 100644 --- a/src/version.ts +++ b/src/version.ts @@ -1 +1 @@ -export const VERSION = '4.61.1'; // x-release-please-version +export const VERSION = '4.62.0'; // x-release-please-version diff --git a/tests/qs/empty-keys-cases.ts b/tests/qs/empty-keys-cases.ts new file mode 100644 index 000000000..ea2c1b0a2 --- /dev/null +++ b/tests/qs/empty-keys-cases.ts @@ -0,0 +1,271 @@ +export const empty_test_cases = [ + { + input: '&', + with_empty_keys: {}, + stringify_output: { + brackets: '', + indices: '', + repeat: '', + }, + no_empty_keys: {}, + }, + { + input: '&&', + with_empty_keys: {}, + stringify_output: { + brackets: '', + indices: '', + repeat: '', + }, + no_empty_keys: {}, + }, + { + input: '&=', + with_empty_keys: { '': '' }, + stringify_output: { + brackets: '=', + indices: '=', + repeat: '=', + }, + no_empty_keys: {}, + }, + { + input: '&=&', + with_empty_keys: { '': '' }, + stringify_output: { + brackets: '=', + indices: '=', + repeat: '=', + }, + no_empty_keys: {}, + }, + { + input: '&=&=', + with_empty_keys: { '': ['', ''] }, + stringify_output: { + brackets: '[]=&[]=', + indices: '[0]=&[1]=', + repeat: '=&=', + }, + no_empty_keys: {}, + }, + { + input: '&=&=&', + with_empty_keys: { '': ['', ''] }, + stringify_output: { + brackets: '[]=&[]=', + indices: '[0]=&[1]=', + repeat: '=&=', + }, + no_empty_keys: {}, + }, + { + input: '=', + with_empty_keys: { '': '' }, + no_empty_keys: {}, + stringify_output: { + brackets: '=', + indices: '=', + repeat: '=', + }, + }, + { + input: '=&', + with_empty_keys: { '': '' }, + stringify_output: { + brackets: '=', + indices: '=', + repeat: '=', + }, + no_empty_keys: {}, + }, + { + input: '=&&&', + with_empty_keys: { '': '' }, + stringify_output: { + brackets: '=', + indices: '=', + repeat: '=', + }, + no_empty_keys: {}, + }, + { + input: '=&=&=&', + with_empty_keys: { '': ['', '', ''] }, + stringify_output: { + brackets: '[]=&[]=&[]=', + indices: '[0]=&[1]=&[2]=', + repeat: '=&=&=', + }, + no_empty_keys: {}, + }, + { + input: '=&a[]=b&a[1]=c', + with_empty_keys: { '': '', a: ['b', 'c'] }, + stringify_output: { + brackets: '=&a[]=b&a[]=c', + indices: '=&a[0]=b&a[1]=c', + repeat: '=&a=b&a=c', + }, + no_empty_keys: { a: ['b', 'c'] }, + }, + { + input: '=a', + with_empty_keys: { '': 'a' }, + no_empty_keys: {}, + stringify_output: { + brackets: '=a', + indices: '=a', + repeat: '=a', + }, + }, + { + input: 'a==a', + with_empty_keys: { a: '=a' }, + no_empty_keys: { a: '=a' }, + stringify_output: { + brackets: 'a==a', + indices: 'a==a', + repeat: 'a==a', + }, + }, + { + input: '=&a[]=b', + with_empty_keys: { '': '', a: ['b'] }, + stringify_output: { + brackets: '=&a[]=b', + indices: '=&a[0]=b', + repeat: '=&a=b', + }, + no_empty_keys: { a: ['b'] }, + }, + { + input: '=&a[]=b&a[]=c&a[2]=d', + with_empty_keys: { '': '', a: ['b', 'c', 'd'] }, + stringify_output: { + brackets: '=&a[]=b&a[]=c&a[]=d', + indices: '=&a[0]=b&a[1]=c&a[2]=d', + repeat: '=&a=b&a=c&a=d', + }, + no_empty_keys: { a: ['b', 'c', 'd'] }, + }, + { + input: '=a&=b', + with_empty_keys: { '': ['a', 'b'] }, + stringify_output: { + brackets: '[]=a&[]=b', + indices: '[0]=a&[1]=b', + repeat: '=a&=b', + }, + no_empty_keys: {}, + }, + { + input: '=a&foo=b', + with_empty_keys: { '': 'a', foo: 'b' }, + no_empty_keys: { foo: 'b' }, + stringify_output: { + brackets: '=a&foo=b', + indices: '=a&foo=b', + repeat: '=a&foo=b', + }, + }, + { + input: 'a[]=b&a=c&=', + with_empty_keys: { '': '', a: ['b', 'c'] }, + stringify_output: { + brackets: '=&a[]=b&a[]=c', + indices: '=&a[0]=b&a[1]=c', + repeat: '=&a=b&a=c', + }, + no_empty_keys: { a: ['b', 'c'] }, + }, + { + input: 'a[]=b&a=c&=', + with_empty_keys: { '': '', a: ['b', 'c'] }, + stringify_output: { + brackets: '=&a[]=b&a[]=c', + indices: '=&a[0]=b&a[1]=c', + repeat: '=&a=b&a=c', + }, + no_empty_keys: { a: ['b', 'c'] }, + }, + { + input: 'a[0]=b&a=c&=', + with_empty_keys: { '': '', a: ['b', 'c'] }, + stringify_output: { + brackets: '=&a[]=b&a[]=c', + indices: '=&a[0]=b&a[1]=c', + repeat: '=&a=b&a=c', + }, + no_empty_keys: { a: ['b', 'c'] }, + }, + { + input: 'a=b&a[]=c&=', + with_empty_keys: { '': '', a: ['b', 'c'] }, + stringify_output: { + brackets: '=&a[]=b&a[]=c', + indices: '=&a[0]=b&a[1]=c', + repeat: '=&a=b&a=c', + }, + no_empty_keys: { a: ['b', 'c'] }, + }, + { + input: 'a=b&a[0]=c&=', + with_empty_keys: { '': '', a: ['b', 'c'] }, + stringify_output: { + brackets: '=&a[]=b&a[]=c', + indices: '=&a[0]=b&a[1]=c', + repeat: '=&a=b&a=c', + }, + no_empty_keys: { a: ['b', 'c'] }, + }, + { + input: '[]=a&[]=b& []=1', + with_empty_keys: { '': ['a', 'b'], ' ': ['1'] }, + stringify_output: { + brackets: '[]=a&[]=b& []=1', + indices: '[0]=a&[1]=b& [0]=1', + repeat: '=a&=b& =1', + }, + no_empty_keys: { 0: 'a', 1: 'b', ' ': ['1'] }, + }, + { + input: '[0]=a&[1]=b&a[0]=1&a[1]=2', + with_empty_keys: { '': ['a', 'b'], a: ['1', '2'] }, + no_empty_keys: { 0: 'a', 1: 'b', a: ['1', '2'] }, + stringify_output: { + brackets: '[]=a&[]=b&a[]=1&a[]=2', + indices: '[0]=a&[1]=b&a[0]=1&a[1]=2', + repeat: '=a&=b&a=1&a=2', + }, + }, + { + input: '[deep]=a&[deep]=2', + with_empty_keys: { '': { deep: ['a', '2'] } }, + stringify_output: { + brackets: '[deep][]=a&[deep][]=2', + indices: '[deep][0]=a&[deep][1]=2', + repeat: '[deep]=a&[deep]=2', + }, + no_empty_keys: { deep: ['a', '2'] }, + }, + { + input: '%5B0%5D=a&%5B1%5D=b', + with_empty_keys: { '': ['a', 'b'] }, + stringify_output: { + brackets: '[]=a&[]=b', + indices: '[0]=a&[1]=b', + repeat: '=a&=b', + }, + no_empty_keys: { 0: 'a', 1: 'b' }, + }, +] satisfies { + input: string; + with_empty_keys: Record; + stringify_output: { + brackets: string; + indices: string; + repeat: string; + }; + no_empty_keys: Record; +}[]; diff --git a/tests/qs/stringify.test.ts b/tests/qs/stringify.test.ts new file mode 100644 index 000000000..ab3456824 --- /dev/null +++ b/tests/qs/stringify.test.ts @@ -0,0 +1,2232 @@ +import iconv from 'iconv-lite'; +import { stringify } from 'openai/internal/qs'; +import { encode } from 'openai/internal/qs/utils'; +import { StringifyOptions } from 'openai/internal/qs/types'; +import { empty_test_cases } from './empty-keys-cases'; +import assert from 'assert'; + +describe('stringify()', function () { + test('stringifies a querystring object', function () { + expect(stringify({ a: 'b' })).toBe('a=b'); + expect(stringify({ a: 1 })).toBe('a=1'); + expect(stringify({ a: 1, b: 2 })).toBe('a=1&b=2'); + expect(stringify({ a: 'A_Z' })).toBe('a=A_Z'); + expect(stringify({ a: '€' })).toBe('a=%E2%82%AC'); + expect(stringify({ a: '' })).toBe('a=%EE%80%80'); + expect(stringify({ a: 'א' })).toBe('a=%D7%90'); + expect(stringify({ a: '𐐷' })).toBe('a=%F0%90%90%B7'); + }); + + test('stringifies falsy values', function () { + expect(stringify(undefined)).toBe(''); + expect(stringify(null)).toBe(''); + expect(stringify(null, { strictNullHandling: true })).toBe(''); + expect(stringify(false)).toBe(''); + expect(stringify(0)).toBe(''); + }); + + test('stringifies symbols', function () { + expect(stringify(Symbol.iterator)).toBe(''); + expect(stringify([Symbol.iterator])).toBe('0=Symbol%28Symbol.iterator%29'); + expect(stringify({ a: Symbol.iterator })).toBe('a=Symbol%28Symbol.iterator%29'); + expect(stringify({ a: [Symbol.iterator] }, { encodeValuesOnly: true, arrayFormat: 'brackets' })).toBe( + 'a[]=Symbol%28Symbol.iterator%29', + ); + }); + + test('stringifies bigints', function () { + var three = BigInt(3); + // @ts-expect-error + var encodeWithN = function (value, defaultEncoder, charset) { + var result = defaultEncoder(value, defaultEncoder, charset); + return typeof value === 'bigint' ? result + 'n' : result; + }; + + expect(stringify(three)).toBe(''); + expect(stringify([three])).toBe('0=3'); + expect(stringify([three], { encoder: encodeWithN })).toBe('0=3n'); + expect(stringify({ a: three })).toBe('a=3'); + expect(stringify({ a: three }, { encoder: encodeWithN })).toBe('a=3n'); + expect(stringify({ a: [three] }, { encodeValuesOnly: true, arrayFormat: 'brackets' })).toBe('a[]=3'); + expect( + stringify({ a: [three] }, { encodeValuesOnly: true, encoder: encodeWithN, arrayFormat: 'brackets' }), + ).toBe('a[]=3n'); + }); + + test('encodes dot in key of object when encodeDotInKeys and allowDots is provided', function () { + expect( + stringify({ 'name.obj': { first: 'John', last: 'Doe' } }, { allowDots: false, encodeDotInKeys: false }), + ).toBe('name.obj%5Bfirst%5D=John&name.obj%5Blast%5D=Doe'); + expect( + stringify({ 'name.obj': { first: 'John', last: 'Doe' } }, { allowDots: true, encodeDotInKeys: false }), + ).toBe('name.obj.first=John&name.obj.last=Doe'); + expect( + stringify({ 'name.obj': { first: 'John', last: 'Doe' } }, { allowDots: false, encodeDotInKeys: true }), + ).toBe('name%252Eobj%5Bfirst%5D=John&name%252Eobj%5Blast%5D=Doe'); + expect( + stringify({ 'name.obj': { first: 'John', last: 'Doe' } }, { allowDots: true, encodeDotInKeys: true }), + ).toBe('name%252Eobj.first=John&name%252Eobj.last=Doe'); + + // st.equal( + // stringify( + // { 'name.obj.subobject': { 'first.godly.name': 'John', last: 'Doe' } }, + // { allowDots: false, encodeDotInKeys: false }, + // ), + // 'name.obj.subobject%5Bfirst.godly.name%5D=John&name.obj.subobject%5Blast%5D=Doe', + // 'with allowDots false and encodeDotInKeys false', + // ); + // st.equal( + // stringify( + // { 'name.obj.subobject': { 'first.godly.name': 'John', last: 'Doe' } }, + // { allowDots: true, encodeDotInKeys: false }, + // ), + // 'name.obj.subobject.first.godly.name=John&name.obj.subobject.last=Doe', + // 'with allowDots false and encodeDotInKeys false', + // ); + // st.equal( + // stringify( + // { 'name.obj.subobject': { 'first.godly.name': 'John', last: 'Doe' } }, + // { allowDots: false, encodeDotInKeys: true }, + // ), + // 'name%252Eobj%252Esubobject%5Bfirst.godly.name%5D=John&name%252Eobj%252Esubobject%5Blast%5D=Doe', + // 'with allowDots false and encodeDotInKeys true', + // ); + // st.equal( + // stringify( + // { 'name.obj.subobject': { 'first.godly.name': 'John', last: 'Doe' } }, + // { allowDots: true, encodeDotInKeys: true }, + // ), + // 'name%252Eobj%252Esubobject.first%252Egodly%252Ename=John&name%252Eobj%252Esubobject.last=Doe', + // 'with allowDots true and encodeDotInKeys true', + // ); + expect( + stringify( + { 'name.obj.subobject': { 'first.godly.name': 'John', last: 'Doe' } }, + { allowDots: false, encodeDotInKeys: false }, + ), + ).toBe('name.obj.subobject%5Bfirst.godly.name%5D=John&name.obj.subobject%5Blast%5D=Doe'); + expect( + stringify( + { 'name.obj.subobject': { 'first.godly.name': 'John', last: 'Doe' } }, + { allowDots: true, encodeDotInKeys: false }, + ), + ).toBe('name.obj.subobject.first.godly.name=John&name.obj.subobject.last=Doe'); + expect( + stringify( + { 'name.obj.subobject': { 'first.godly.name': 'John', last: 'Doe' } }, + { allowDots: false, encodeDotInKeys: true }, + ), + ).toBe('name%252Eobj%252Esubobject%5Bfirst.godly.name%5D=John&name%252Eobj%252Esubobject%5Blast%5D=Doe'); + expect( + stringify( + { 'name.obj.subobject': { 'first.godly.name': 'John', last: 'Doe' } }, + { allowDots: true, encodeDotInKeys: true }, + ), + ).toBe('name%252Eobj%252Esubobject.first%252Egodly%252Ename=John&name%252Eobj%252Esubobject.last=Doe'); + }); + + test('should encode dot in key of object, and automatically set allowDots to `true` when encodeDotInKeys is true and allowDots in undefined', function () { + // st.equal( + // stringify( + // { 'name.obj.subobject': { 'first.godly.name': 'John', last: 'Doe' } }, + // { encodeDotInKeys: true }, + // ), + // 'name%252Eobj%252Esubobject.first%252Egodly%252Ename=John&name%252Eobj%252Esubobject.last=Doe', + // 'with allowDots undefined and encodeDotInKeys true', + // ); + expect( + stringify( + { 'name.obj.subobject': { 'first.godly.name': 'John', last: 'Doe' } }, + { encodeDotInKeys: true }, + ), + ).toBe('name%252Eobj%252Esubobject.first%252Egodly%252Ename=John&name%252Eobj%252Esubobject.last=Doe'); + }); + + test('should encode dot in key of object when encodeDotInKeys and allowDots is provided, and nothing else when encodeValuesOnly is provided', function () { + // st.equal( + // stringify( + // { 'name.obj': { first: 'John', last: 'Doe' } }, + // { + // encodeDotInKeys: true, + // allowDots: true, + // encodeValuesOnly: true, + // }, + // ), + // 'name%2Eobj.first=John&name%2Eobj.last=Doe', + // ); + expect( + stringify( + { 'name.obj': { first: 'John', last: 'Doe' } }, + { + encodeDotInKeys: true, + allowDots: true, + encodeValuesOnly: true, + }, + ), + ).toBe('name%2Eobj.first=John&name%2Eobj.last=Doe'); + + // st.equal( + // stringify( + // { 'name.obj.subobject': { 'first.godly.name': 'John', last: 'Doe' } }, + // { allowDots: true, encodeDotInKeys: true, encodeValuesOnly: true }, + // ), + // 'name%2Eobj%2Esubobject.first%2Egodly%2Ename=John&name%2Eobj%2Esubobject.last=Doe', + // ); + expect( + stringify( + { 'name.obj.subobject': { 'first.godly.name': 'John', last: 'Doe' } }, + { allowDots: true, encodeDotInKeys: true, encodeValuesOnly: true }, + ), + ).toBe('name%2Eobj%2Esubobject.first%2Egodly%2Ename=John&name%2Eobj%2Esubobject.last=Doe'); + }); + + test('throws when `commaRoundTrip` is not a boolean', function () { + // st['throws']( + // function () { + // stringify({}, { commaRoundTrip: 'not a boolean' }); + // }, + // TypeError, + // 'throws when `commaRoundTrip` is not a boolean', + // ); + expect(() => { + // @ts-expect-error + stringify({}, { commaRoundTrip: 'not a boolean' }); + }).toThrow(TypeError); + }); + + test('throws when `encodeDotInKeys` is not a boolean', function () { + // st['throws'](function () { + // stringify({ a: [], b: 'zz' }, { encodeDotInKeys: 'foobar' }); + // }, TypeError); + expect(() => { + // @ts-expect-error + stringify({ a: [], b: 'zz' }, { encodeDotInKeys: 'foobar' }); + }).toThrow(TypeError); + + // st['throws'](function () { + // stringify({ a: [], b: 'zz' }, { encodeDotInKeys: 0 }); + // }, TypeError); + expect(() => { + // @ts-expect-error + stringify({ a: [], b: 'zz' }, { encodeDotInKeys: 0 }); + }).toThrow(TypeError); + + // st['throws'](function () { + // stringify({ a: [], b: 'zz' }, { encodeDotInKeys: NaN }); + // }, TypeError); + expect(() => { + // @ts-expect-error + stringify({ a: [], b: 'zz' }, { encodeDotInKeys: NaN }); + }).toThrow(TypeError); + + // st['throws'](function () { + // stringify({ a: [], b: 'zz' }, { encodeDotInKeys: null }); + // }, TypeError); + expect(() => { + // @ts-expect-error + stringify({ a: [], b: 'zz' }, { encodeDotInKeys: null }); + }).toThrow(TypeError); + }); + + test('adds query prefix', function () { + // st.equal(stringify({ a: 'b' }, { addQueryPrefix: true }), '?a=b'); + expect(stringify({ a: 'b' }, { addQueryPrefix: true })).toBe('?a=b'); + }); + + test('with query prefix, outputs blank string given an empty object', function () { + // st.equal(stringify({}, { addQueryPrefix: true }), ''); + expect(stringify({}, { addQueryPrefix: true })).toBe(''); + }); + + test('stringifies nested falsy values', function () { + // st.equal(stringify({ a: { b: { c: null } } }), 'a%5Bb%5D%5Bc%5D='); + // st.equal( + // stringify({ a: { b: { c: null } } }, { strictNullHandling: true }), + // 'a%5Bb%5D%5Bc%5D', + // ); + // st.equal(stringify({ a: { b: { c: false } } }), 'a%5Bb%5D%5Bc%5D=false'); + expect(stringify({ a: { b: { c: null } } })).toBe('a%5Bb%5D%5Bc%5D='); + expect(stringify({ a: { b: { c: null } } }, { strictNullHandling: true })).toBe('a%5Bb%5D%5Bc%5D'); + expect(stringify({ a: { b: { c: false } } })).toBe('a%5Bb%5D%5Bc%5D=false'); + }); + + test('stringifies a nested object', function () { + // st.equal(stringify({ a: { b: 'c' } }), 'a%5Bb%5D=c'); + // st.equal(stringify({ a: { b: { c: { d: 'e' } } } }), 'a%5Bb%5D%5Bc%5D%5Bd%5D=e'); + expect(stringify({ a: { b: 'c' } })).toBe('a%5Bb%5D=c'); + expect(stringify({ a: { b: { c: { d: 'e' } } } })).toBe('a%5Bb%5D%5Bc%5D%5Bd%5D=e'); + }); + + test('`allowDots` option: stringifies a nested object with dots notation', function () { + // st.equal(stringify({ a: { b: 'c' } }, { allowDots: true }), 'a.b=c'); + // st.equal(stringify({ a: { b: { c: { d: 'e' } } } }, { allowDots: true }), 'a.b.c.d=e'); + expect(stringify({ a: { b: 'c' } }, { allowDots: true })).toBe('a.b=c'); + expect(stringify({ a: { b: { c: { d: 'e' } } } }, { allowDots: true })).toBe('a.b.c.d=e'); + }); + + test('stringifies an array value', function () { + // st.equal( + // stringify({ a: ['b', 'c', 'd'] }, { arrayFormat: 'indices' }), + // 'a%5B0%5D=b&a%5B1%5D=c&a%5B2%5D=d', + // 'indices => indices', + // ); + // st.equal( + // stringify({ a: ['b', 'c', 'd'] }, { arrayFormat: 'brackets' }), + // 'a%5B%5D=b&a%5B%5D=c&a%5B%5D=d', + // 'brackets => brackets', + // ); + // st.equal( + // stringify({ a: ['b', 'c', 'd'] }, { arrayFormat: 'comma' }), + // 'a=b%2Cc%2Cd', + // 'comma => comma', + // ); + // st.equal( + // stringify({ a: ['b', 'c', 'd'] }, { arrayFormat: 'comma', commaRoundTrip: true }), + // 'a=b%2Cc%2Cd', + // 'comma round trip => comma', + // ); + // st.equal( + // stringify({ a: ['b', 'c', 'd'] }), + // 'a%5B0%5D=b&a%5B1%5D=c&a%5B2%5D=d', + // 'default => indices', + // ); + expect(stringify({ a: ['b', 'c', 'd'] }, { arrayFormat: 'indices' })).toBe( + 'a%5B0%5D=b&a%5B1%5D=c&a%5B2%5D=d', + ); + expect(stringify({ a: ['b', 'c', 'd'] }, { arrayFormat: 'brackets' })).toBe( + 'a%5B%5D=b&a%5B%5D=c&a%5B%5D=d', + ); + expect(stringify({ a: ['b', 'c', 'd'] }, { arrayFormat: 'comma' })).toBe('a=b%2Cc%2Cd'); + expect(stringify({ a: ['b', 'c', 'd'] }, { arrayFormat: 'comma', commaRoundTrip: true })).toBe( + 'a=b%2Cc%2Cd', + ); + expect(stringify({ a: ['b', 'c', 'd'] })).toBe('a%5B0%5D=b&a%5B1%5D=c&a%5B2%5D=d'); + }); + + test('`skipNulls` option', function () { + // st.equal( + // stringify({ a: 'b', c: null }, { skipNulls: true }), + // 'a=b', + // 'omits nulls when asked', + // ); + expect(stringify({ a: 'b', c: null }, { skipNulls: true })).toBe('a=b'); + + // st.equal( + // stringify({ a: { b: 'c', d: null } }, { skipNulls: true }), + // 'a%5Bb%5D=c', + // 'omits nested nulls when asked', + // ); + expect(stringify({ a: { b: 'c', d: null } }, { skipNulls: true })).toBe('a%5Bb%5D=c'); + }); + + test('omits array indices when asked', function () { + // st.equal(stringify({ a: ['b', 'c', 'd'] }, { indices: false }), 'a=b&a=c&a=d'); + expect(stringify({ a: ['b', 'c', 'd'] }, { indices: false })).toBe('a=b&a=c&a=d'); + }); + + test('omits object key/value pair when value is empty array', function () { + // st.equal(stringify({ a: [], b: 'zz' }), 'b=zz'); + expect(stringify({ a: [], b: 'zz' })).toBe('b=zz'); + }); + + test('should not omit object key/value pair when value is empty array and when asked', function () { + // st.equal(stringify({ a: [], b: 'zz' }), 'b=zz'); + // st.equal(stringify({ a: [], b: 'zz' }, { allowEmptyArrays: false }), 'b=zz'); + // st.equal(stringify({ a: [], b: 'zz' }, { allowEmptyArrays: true }), 'a[]&b=zz'); + expect(stringify({ a: [], b: 'zz' })).toBe('b=zz'); + expect(stringify({ a: [], b: 'zz' }, { allowEmptyArrays: false })).toBe('b=zz'); + expect(stringify({ a: [], b: 'zz' }, { allowEmptyArrays: true })).toBe('a[]&b=zz'); + }); + + test('should throw when allowEmptyArrays is not of type boolean', function () { + // st['throws'](function () { + // stringify({ a: [], b: 'zz' }, { allowEmptyArrays: 'foobar' }); + // }, TypeError); + expect(() => { + // @ts-expect-error + stringify({ a: [], b: 'zz' }, { allowEmptyArrays: 'foobar' }); + }).toThrow(TypeError); + + // st['throws'](function () { + // stringify({ a: [], b: 'zz' }, { allowEmptyArrays: 0 }); + // }, TypeError); + expect(() => { + // @ts-expect-error + stringify({ a: [], b: 'zz' }, { allowEmptyArrays: 0 }); + }).toThrow(TypeError); + + // st['throws'](function () { + // stringify({ a: [], b: 'zz' }, { allowEmptyArrays: NaN }); + // }, TypeError); + expect(() => { + // @ts-expect-error + stringify({ a: [], b: 'zz' }, { allowEmptyArrays: NaN }); + }).toThrow(TypeError); + + // st['throws'](function () { + // stringify({ a: [], b: 'zz' }, { allowEmptyArrays: null }); + // }, TypeError); + expect(() => { + // @ts-expect-error + stringify({ a: [], b: 'zz' }, { allowEmptyArrays: null }); + }).toThrow(TypeError); + }); + + test('allowEmptyArrays + strictNullHandling', function () { + // st.equal( + // stringify({ testEmptyArray: [] }, { strictNullHandling: true, allowEmptyArrays: true }), + // 'testEmptyArray[]', + // ); + expect(stringify({ testEmptyArray: [] }, { strictNullHandling: true, allowEmptyArrays: true })).toBe( + 'testEmptyArray[]', + ); + }); + + describe('stringifies an array value with one item vs multiple items', function () { + test('non-array item', function () { + // s2t.equal( + // stringify({ a: 'c' }, { encodeValuesOnly: true, arrayFormat: 'indices' }), + // 'a=c', + // ); + // s2t.equal( + // stringify({ a: 'c' }, { encodeValuesOnly: true, arrayFormat: 'brackets' }), + // 'a=c', + // ); + // s2t.equal(stringify({ a: 'c' }, { encodeValuesOnly: true, arrayFormat: 'comma' }), 'a=c'); + // s2t.equal(stringify({ a: 'c' }, { encodeValuesOnly: true }), 'a=c'); + expect(stringify({ a: 'c' }, { encodeValuesOnly: true, arrayFormat: 'indices' })).toBe('a=c'); + expect(stringify({ a: 'c' }, { encodeValuesOnly: true, arrayFormat: 'brackets' })).toBe('a=c'); + expect(stringify({ a: 'c' }, { encodeValuesOnly: true, arrayFormat: 'comma' })).toBe('a=c'); + expect(stringify({ a: 'c' }, { encodeValuesOnly: true })).toBe('a=c'); + }); + + test('array with a single item', function () { + // s2t.equal( + // stringify({ a: ['c'] }, { encodeValuesOnly: true, arrayFormat: 'indices' }), + // 'a[0]=c', + // ); + // s2t.equal( + // stringify({ a: ['c'] }, { encodeValuesOnly: true, arrayFormat: 'brackets' }), + // 'a[]=c', + // ); + // s2t.equal( + // stringify({ a: ['c'] }, { encodeValuesOnly: true, arrayFormat: 'comma' }), + // 'a=c', + // ); + // s2t.equal( + // stringify( + // { a: ['c'] }, + // { encodeValuesOnly: true, arrayFormat: 'comma', commaRoundTrip: true }, + // ), + // 'a[]=c', + // ); // so it parses back as an array + // s2t.equal(stringify({ a: ['c'] }, { encodeValuesOnly: true }), 'a[0]=c'); + expect(stringify({ a: ['c'] }, { encodeValuesOnly: true, arrayFormat: 'indices' })).toBe('a[0]=c'); + expect(stringify({ a: ['c'] }, { encodeValuesOnly: true, arrayFormat: 'brackets' })).toBe('a[]=c'); + expect(stringify({ a: ['c'] }, { encodeValuesOnly: true, arrayFormat: 'comma' })).toBe('a=c'); + expect( + stringify({ a: ['c'] }, { encodeValuesOnly: true, arrayFormat: 'comma', commaRoundTrip: true }), + ).toBe('a[]=c'); + expect(stringify({ a: ['c'] }, { encodeValuesOnly: true })).toBe('a[0]=c'); + }); + + test('array with multiple items', function () { + // s2t.equal( + // stringify({ a: ['c', 'd'] }, { encodeValuesOnly: true, arrayFormat: 'indices' }), + // 'a[0]=c&a[1]=d', + // ); + // s2t.equal( + // stringify({ a: ['c', 'd'] }, { encodeValuesOnly: true, arrayFormat: 'brackets' }), + // 'a[]=c&a[]=d', + // ); + // s2t.equal( + // stringify({ a: ['c', 'd'] }, { encodeValuesOnly: true, arrayFormat: 'comma' }), + // 'a=c,d', + // ); + // s2t.equal( + // stringify( + // { a: ['c', 'd'] }, + // { encodeValuesOnly: true, arrayFormat: 'comma', commaRoundTrip: true }, + // ), + // 'a=c,d', + // ); + // s2t.equal(stringify({ a: ['c', 'd'] }, { encodeValuesOnly: true }), 'a[0]=c&a[1]=d'); + expect(stringify({ a: ['c', 'd'] }, { encodeValuesOnly: true, arrayFormat: 'indices' })).toBe( + 'a[0]=c&a[1]=d', + ); + expect(stringify({ a: ['c', 'd'] }, { encodeValuesOnly: true, arrayFormat: 'brackets' })).toBe( + 'a[]=c&a[]=d', + ); + expect(stringify({ a: ['c', 'd'] }, { encodeValuesOnly: true, arrayFormat: 'comma' })).toBe('a=c,d'); + expect( + stringify({ a: ['c', 'd'] }, { encodeValuesOnly: true, arrayFormat: 'comma', commaRoundTrip: true }), + ).toBe('a=c,d'); + expect(stringify({ a: ['c', 'd'] }, { encodeValuesOnly: true })).toBe('a[0]=c&a[1]=d'); + }); + + test('array with multiple items with a comma inside', function () { + // s2t.equal( + // stringify({ a: ['c,d', 'e'] }, { encodeValuesOnly: true, arrayFormat: 'comma' }), + // 'a=c%2Cd,e', + // ); + // s2t.equal(stringify({ a: ['c,d', 'e'] }, { arrayFormat: 'comma' }), 'a=c%2Cd%2Ce'); + expect(stringify({ a: ['c,d', 'e'] }, { encodeValuesOnly: true, arrayFormat: 'comma' })).toBe( + 'a=c%2Cd,e', + ); + expect(stringify({ a: ['c,d', 'e'] }, { arrayFormat: 'comma' })).toBe('a=c%2Cd%2Ce'); + + // s2t.equal( + // stringify( + // { a: ['c,d', 'e'] }, + // { encodeValuesOnly: true, arrayFormat: 'comma', commaRoundTrip: true }, + // ), + // 'a=c%2Cd,e', + // ); + // s2t.equal( + // stringify({ a: ['c,d', 'e'] }, { arrayFormat: 'comma', commaRoundTrip: true }), + // 'a=c%2Cd%2Ce', + // ); + expect( + stringify( + { a: ['c,d', 'e'] }, + { encodeValuesOnly: true, arrayFormat: 'comma', commaRoundTrip: true }, + ), + ).toBe('a=c%2Cd,e'); + expect(stringify({ a: ['c,d', 'e'] }, { arrayFormat: 'comma', commaRoundTrip: true })).toBe( + 'a=c%2Cd%2Ce', + ); + }); + }); + + test('stringifies a nested array value', function () { + expect(stringify({ a: { b: ['c', 'd'] } }, { encodeValuesOnly: true, arrayFormat: 'indices' })).toBe( + 'a[b][0]=c&a[b][1]=d', + ); + expect(stringify({ a: { b: ['c', 'd'] } }, { encodeValuesOnly: true, arrayFormat: 'brackets' })).toBe( + 'a[b][]=c&a[b][]=d', + ); + expect(stringify({ a: { b: ['c', 'd'] } }, { encodeValuesOnly: true, arrayFormat: 'comma' })).toBe( + 'a[b]=c,d', + ); + expect(stringify({ a: { b: ['c', 'd'] } }, { encodeValuesOnly: true })).toBe('a[b][0]=c&a[b][1]=d'); + }); + + test('stringifies comma and empty array values', function () { + // st.equal( + // stringify({ a: [',', '', 'c,d%'] }, { encode: false, arrayFormat: 'indices' }), + // 'a[0]=,&a[1]=&a[2]=c,d%', + // ); + // st.equal( + // stringify({ a: [',', '', 'c,d%'] }, { encode: false, arrayFormat: 'brackets' }), + // 'a[]=,&a[]=&a[]=c,d%', + // ); + // st.equal( + // stringify({ a: [',', '', 'c,d%'] }, { encode: false, arrayFormat: 'comma' }), + // 'a=,,,c,d%', + // ); + // st.equal( + // stringify({ a: [',', '', 'c,d%'] }, { encode: false, arrayFormat: 'repeat' }), + // 'a=,&a=&a=c,d%', + // ); + expect(stringify({ a: [',', '', 'c,d%'] }, { encode: false, arrayFormat: 'indices' })).toBe( + 'a[0]=,&a[1]=&a[2]=c,d%', + ); + expect(stringify({ a: [',', '', 'c,d%'] }, { encode: false, arrayFormat: 'brackets' })).toBe( + 'a[]=,&a[]=&a[]=c,d%', + ); + expect(stringify({ a: [',', '', 'c,d%'] }, { encode: false, arrayFormat: 'comma' })).toBe('a=,,,c,d%'); + expect(stringify({ a: [',', '', 'c,d%'] }, { encode: false, arrayFormat: 'repeat' })).toBe( + 'a=,&a=&a=c,d%', + ); + + // st.equal( + // stringify( + // { a: [',', '', 'c,d%'] }, + // { encode: true, encodeValuesOnly: true, arrayFormat: 'indices' }, + // ), + // 'a[0]=%2C&a[1]=&a[2]=c%2Cd%25', + // ); + // st.equal( + // stringify( + // { a: [',', '', 'c,d%'] }, + // { encode: true, encodeValuesOnly: true, arrayFormat: 'brackets' }, + // ), + // 'a[]=%2C&a[]=&a[]=c%2Cd%25', + // ); + // st.equal( + // stringify( + // { a: [',', '', 'c,d%'] }, + // { encode: true, encodeValuesOnly: true, arrayFormat: 'comma' }, + // ), + // 'a=%2C,,c%2Cd%25', + // ); + // st.equal( + // stringify( + // { a: [',', '', 'c,d%'] }, + // { encode: true, encodeValuesOnly: true, arrayFormat: 'repeat' }, + // ), + // 'a=%2C&a=&a=c%2Cd%25', + // ); + expect( + stringify({ a: [',', '', 'c,d%'] }, { encode: true, encodeValuesOnly: false, arrayFormat: 'indices' }), + ).toBe('a%5B0%5D=%2C&a%5B1%5D=&a%5B2%5D=c%2Cd%25'); + expect( + stringify({ a: [',', '', 'c,d%'] }, { encode: true, encodeValuesOnly: true, arrayFormat: 'brackets' }), + ).toBe('a[]=%2C&a[]=&a[]=c%2Cd%25'); + expect( + stringify({ a: [',', '', 'c,d%'] }, { encode: true, encodeValuesOnly: false, arrayFormat: 'comma' }), + ).toBe('a=%2C%2C%2Cc%2Cd%25'); + expect( + stringify({ a: [',', '', 'c,d%'] }, { encode: true, encodeValuesOnly: false, arrayFormat: 'repeat' }), + ).toBe('a=%2C&a=&a=c%2Cd%25'); + + // st.equal( + // stringify( + // { a: [',', '', 'c,d%'] }, + // { encode: true, encodeValuesOnly: false, arrayFormat: 'indices' }, + // ), + // 'a%5B0%5D=%2C&a%5B1%5D=&a%5B2%5D=c%2Cd%25', + // ); + // st.equal( + // stringify( + // { a: [',', '', 'c,d%'] }, + // { encode: true, encodeValuesOnly: false, arrayFormat: 'brackets' }, + // ), + // 'a%5B%5D=%2C&a%5B%5D=&a%5B%5D=c%2Cd%25', + // ); + // st.equal( + // stringify( + // { a: [',', '', 'c,d%'] }, + // { encode: true, encodeValuesOnly: false, arrayFormat: 'comma' }, + // ), + // 'a=%2C%2C%2Cc%2Cd%25', + // ); + // st.equal( + // stringify( + // { a: [',', '', 'c,d%'] }, + // { encode: true, encodeValuesOnly: false, arrayFormat: 'repeat' }, + // ), + // 'a=%2C&a=&a=c%2Cd%25', + // ); + expect( + stringify({ a: [',', '', 'c,d%'] }, { encode: true, encodeValuesOnly: false, arrayFormat: 'repeat' }), + ).toBe('a=%2C&a=&a=c%2Cd%25'); + expect( + stringify({ a: [',', '', 'c,d%'] }, { encode: true, encodeValuesOnly: false, arrayFormat: 'indices' }), + ).toBe('a%5B0%5D=%2C&a%5B1%5D=&a%5B2%5D=c%2Cd%25'); + expect( + stringify({ a: [',', '', 'c,d%'] }, { encode: true, encodeValuesOnly: true, arrayFormat: 'brackets' }), + ).toBe('a[]=%2C&a[]=&a[]=c%2Cd%25'); + expect( + stringify({ a: [',', '', 'c,d%'] }, { encode: true, encodeValuesOnly: false, arrayFormat: 'comma' }), + ).toBe('a=%2C%2C%2Cc%2Cd%25'); + expect( + stringify({ a: [',', '', 'c,d%'] }, { encode: true, encodeValuesOnly: false, arrayFormat: 'repeat' }), + ).toBe('a=%2C&a=&a=c%2Cd%25'); + }); + + test('stringifies comma and empty non-array values', function () { + // st.equal( + // stringify({ a: ',', b: '', c: 'c,d%' }, { encode: false, arrayFormat: 'indices' }), + // 'a=,&b=&c=c,d%', + // ); + // st.equal( + // stringify({ a: ',', b: '', c: 'c,d%' }, { encode: false, arrayFormat: 'brackets' }), + // 'a=,&b=&c=c,d%', + // ); + // st.equal( + // stringify({ a: ',', b: '', c: 'c,d%' }, { encode: false, arrayFormat: 'comma' }), + // 'a=,&b=&c=c,d%', + // ); + // st.equal( + // stringify({ a: ',', b: '', c: 'c,d%' }, { encode: false, arrayFormat: 'repeat' }), + // 'a=,&b=&c=c,d%', + // ); + expect(stringify({ a: ',', b: '', c: 'c,d%' }, { encode: false, arrayFormat: 'indices' })).toBe( + 'a=,&b=&c=c,d%', + ); + expect(stringify({ a: ',', b: '', c: 'c,d%' }, { encode: false, arrayFormat: 'brackets' })).toBe( + 'a=,&b=&c=c,d%', + ); + + // st.equal( + // stringify( + // { a: ',', b: '', c: 'c,d%' }, + // { encode: true, encodeValuesOnly: true, arrayFormat: 'indices' }, + // ), + // 'a=%2C&b=&c=c%2Cd%25', + // ); + // st.equal( + // stringify( + // { a: ',', b: '', c: 'c,d%' }, + // { encode: true, encodeValuesOnly: true, arrayFormat: 'brackets' }, + // ), + // 'a=%2C&b=&c=c%2Cd%25', + // ); + // st.equal( + // stringify( + // { a: ',', b: '', c: 'c,d%' }, + // { encode: true, encodeValuesOnly: true, arrayFormat: 'comma' }, + // ), + // 'a=%2C&b=&c=c%2Cd%25', + // ); + // st.equal( + // stringify( + // { a: ',', b: '', c: 'c,d%' }, + // { encode: true, encodeValuesOnly: true, arrayFormat: 'repeat' }, + // ), + // 'a=%2C&b=&c=c%2Cd%25', + // ); + expect( + stringify( + { a: ',', b: '', c: 'c,d%' }, + { encode: true, encodeValuesOnly: true, arrayFormat: 'indices' }, + ), + ).toBe('a=%2C&b=&c=c%2Cd%25'); + expect( + stringify( + { a: ',', b: '', c: 'c,d%' }, + { encode: true, encodeValuesOnly: true, arrayFormat: 'brackets' }, + ), + ).toBe('a=%2C&b=&c=c%2Cd%25'); + expect( + stringify({ a: ',', b: '', c: 'c,d%' }, { encode: true, encodeValuesOnly: true, arrayFormat: 'comma' }), + ).toBe('a=%2C&b=&c=c%2Cd%25'); + expect( + stringify( + { a: ',', b: '', c: 'c,d%' }, + { encode: true, encodeValuesOnly: true, arrayFormat: 'repeat' }, + ), + ).toBe('a=%2C&b=&c=c%2Cd%25'); + + // st.equal( + // stringify( + // { a: ',', b: '', c: 'c,d%' }, + // { encode: true, encodeValuesOnly: false, arrayFormat: 'indices' }, + // ), + // 'a=%2C&b=&c=c%2Cd%25', + // ); + // st.equal( + // stringify( + // { a: ',', b: '', c: 'c,d%' }, + // { encode: true, encodeValuesOnly: false, arrayFormat: 'brackets' }, + // ), + // 'a=%2C&b=&c=c%2Cd%25', + // ); + // st.equal( + // stringify( + // { a: ',', b: '', c: 'c,d%' }, + // { encode: true, encodeValuesOnly: false, arrayFormat: 'comma' }, + // ), + // 'a=%2C&b=&c=c%2Cd%25', + // ); + // st.equal( + // stringify( + // { a: ',', b: '', c: 'c,d%' }, + // { encode: true, encodeValuesOnly: false, arrayFormat: 'repeat' }, + // ), + // 'a=%2C&b=&c=c%2Cd%25', + // ); + expect( + stringify( + { a: ',', b: '', c: 'c,d%' }, + { encode: true, encodeValuesOnly: false, arrayFormat: 'indices' }, + ), + ).toBe('a=%2C&b=&c=c%2Cd%25'); + expect( + stringify( + { a: ',', b: '', c: 'c,d%' }, + { encode: true, encodeValuesOnly: false, arrayFormat: 'brackets' }, + ), + ).toBe('a=%2C&b=&c=c%2Cd%25'); + expect( + stringify( + { a: ',', b: '', c: 'c,d%' }, + { encode: true, encodeValuesOnly: false, arrayFormat: 'comma' }, + ), + ).toBe('a=%2C&b=&c=c%2Cd%25'); + expect( + stringify( + { a: ',', b: '', c: 'c,d%' }, + { encode: true, encodeValuesOnly: false, arrayFormat: 'repeat' }, + ), + ).toBe('a=%2C&b=&c=c%2Cd%25'); + }); + + test('stringifies a nested array value with dots notation', function () { + // st.equal( + // stringify( + // { a: { b: ['c', 'd'] } }, + // { allowDots: true, encodeValuesOnly: true, arrayFormat: 'indices' }, + // ), + // 'a.b[0]=c&a.b[1]=d', + // 'indices: stringifies with dots + indices', + // ); + // st.equal( + // stringify( + // { a: { b: ['c', 'd'] } }, + // { allowDots: true, encodeValuesOnly: true, arrayFormat: 'brackets' }, + // ), + // 'a.b[]=c&a.b[]=d', + // 'brackets: stringifies with dots + brackets', + // ); + // st.equal( + // stringify( + // { a: { b: ['c', 'd'] } }, + // { allowDots: true, encodeValuesOnly: true, arrayFormat: 'comma' }, + // ), + // 'a.b=c,d', + // 'comma: stringifies with dots + comma', + // ); + // st.equal( + // stringify({ a: { b: ['c', 'd'] } }, { allowDots: true, encodeValuesOnly: true }), + // 'a.b[0]=c&a.b[1]=d', + // 'default: stringifies with dots + indices', + // ); + expect( + stringify( + { a: { b: ['c', 'd'] } }, + { allowDots: true, encodeValuesOnly: true, arrayFormat: 'indices' }, + ), + ).toBe('a.b[0]=c&a.b[1]=d'); + expect( + stringify( + { a: { b: ['c', 'd'] } }, + { allowDots: true, encodeValuesOnly: true, arrayFormat: 'brackets' }, + ), + ).toBe('a.b[]=c&a.b[]=d'); + expect( + stringify({ a: { b: ['c', 'd'] } }, { allowDots: true, encodeValuesOnly: true, arrayFormat: 'comma' }), + ).toBe('a.b=c,d'); + expect(stringify({ a: { b: ['c', 'd'] } }, { allowDots: true, encodeValuesOnly: true })).toBe( + 'a.b[0]=c&a.b[1]=d', + ); + }); + + test('stringifies an object inside an array', function () { + // st.equal( + // stringify({ a: [{ b: 'c' }] }, { arrayFormat: 'indices', encodeValuesOnly: true }), + // 'a[0][b]=c', + // 'indices => indices', + // ); + // st.equal( + // stringify({ a: [{ b: 'c' }] }, { arrayFormat: 'repeat', encodeValuesOnly: true }), + // 'a[b]=c', + // 'repeat => repeat', + // ); + // st.equal( + // stringify({ a: [{ b: 'c' }] }, { arrayFormat: 'brackets', encodeValuesOnly: true }), + // 'a[][b]=c', + // 'brackets => brackets', + // ); + // st.equal( + // stringify({ a: [{ b: 'c' }] }, { encodeValuesOnly: true }), + // 'a[0][b]=c', + // 'default => indices', + // ); + expect(stringify({ a: [{ b: 'c' }] }, { arrayFormat: 'indices', encodeValuesOnly: true })).toBe( + 'a[0][b]=c', + ); + expect(stringify({ a: [{ b: 'c' }] }, { arrayFormat: 'repeat', encodeValuesOnly: true })).toBe('a[b]=c'); + expect(stringify({ a: [{ b: 'c' }] }, { arrayFormat: 'brackets', encodeValuesOnly: true })).toBe( + 'a[][b]=c', + ); + expect(stringify({ a: [{ b: 'c' }] }, { encodeValuesOnly: true })).toBe('a[0][b]=c'); + + // st.equal( + // stringify({ a: [{ b: { c: [1] } }] }, { arrayFormat: 'indices', encodeValuesOnly: true }), + // 'a[0][b][c][0]=1', + // 'indices => indices', + // ); + // st.equal( + // stringify({ a: [{ b: { c: [1] } }] }, { arrayFormat: 'repeat', encodeValuesOnly: true }), + // 'a[b][c]=1', + // 'repeat => repeat', + // ); + // st.equal( + // stringify({ a: [{ b: { c: [1] } }] }, { arrayFormat: 'brackets', encodeValuesOnly: true }), + // 'a[][b][c][]=1', + // 'brackets => brackets', + // ); + // st.equal( + // stringify({ a: [{ b: { c: [1] } }] }, { encodeValuesOnly: true }), + // 'a[0][b][c][0]=1', + // 'default => indices', + // ); + expect(stringify({ a: [{ b: { c: [1] } }] }, { arrayFormat: 'indices', encodeValuesOnly: true })).toBe( + 'a[0][b][c][0]=1', + ); + expect(stringify({ a: [{ b: { c: [1] } }] }, { arrayFormat: 'repeat', encodeValuesOnly: true })).toBe( + 'a[b][c]=1', + ); + expect(stringify({ a: [{ b: { c: [1] } }] }, { arrayFormat: 'brackets', encodeValuesOnly: true })).toBe( + 'a[][b][c][]=1', + ); + expect(stringify({ a: [{ b: { c: [1] } }] }, { encodeValuesOnly: true })).toBe('a[0][b][c][0]=1'); + }); + + test('stringifies an array with mixed objects and primitives', function () { + // st.equal( + // stringify({ a: [{ b: 1 }, 2, 3] }, { encodeValuesOnly: true, arrayFormat: 'indices' }), + // 'a[0][b]=1&a[1]=2&a[2]=3', + // 'indices => indices', + // ); + // st.equal( + // stringify({ a: [{ b: 1 }, 2, 3] }, { encodeValuesOnly: true, arrayFormat: 'brackets' }), + // 'a[][b]=1&a[]=2&a[]=3', + // 'brackets => brackets', + // ); + // st.equal( + // stringify({ a: [{ b: 1 }, 2, 3] }, { encodeValuesOnly: true, arrayFormat: 'comma' }), + // '???', + // 'brackets => brackets', + // { skip: 'TODO: figure out what this should do' }, + // ); + // st.equal( + // stringify({ a: [{ b: 1 }, 2, 3] }, { encodeValuesOnly: true }), + // 'a[0][b]=1&a[1]=2&a[2]=3', + // 'default => indices', + // ); + expect(stringify({ a: [{ b: 1 }, 2, 3] }, { encodeValuesOnly: true, arrayFormat: 'indices' })).toBe( + 'a[0][b]=1&a[1]=2&a[2]=3', + ); + expect(stringify({ a: [{ b: 1 }, 2, 3] }, { encodeValuesOnly: true, arrayFormat: 'brackets' })).toBe( + 'a[][b]=1&a[]=2&a[]=3', + ); + // !Skipped: Figure out what this should do + // expect( + // stringify({ a: [{ b: 1 }, 2, 3] }, { encodeValuesOnly: true, arrayFormat: 'comma' }), + // ).toBe('???'); + expect(stringify({ a: [{ b: 1 }, 2, 3] }, { encodeValuesOnly: true })).toBe('a[0][b]=1&a[1]=2&a[2]=3'); + }); + + test('stringifies an object inside an array with dots notation', function () { + // st.equal( + // stringify({ a: [{ b: 'c' }] }, { allowDots: true, encode: false, arrayFormat: 'indices' }), + // 'a[0].b=c', + // 'indices => indices', + // ); + // st.equal( + // stringify( + // { a: [{ b: 'c' }] }, + // { allowDots: true, encode: false, arrayFormat: 'brackets' }, + // ), + // 'a[].b=c', + // 'brackets => brackets', + // ); + // st.equal( + // stringify({ a: [{ b: 'c' }] }, { allowDots: true, encode: false }), + // 'a[0].b=c', + // 'default => indices', + // ); + expect(stringify({ a: [{ b: 'c' }] }, { allowDots: true, encode: false, arrayFormat: 'indices' })).toBe( + 'a[0].b=c', + ); + expect(stringify({ a: [{ b: 'c' }] }, { allowDots: true, encode: false, arrayFormat: 'brackets' })).toBe( + 'a[].b=c', + ); + expect(stringify({ a: [{ b: 'c' }] }, { allowDots: true, encode: false })).toBe('a[0].b=c'); + + // st.equal( + // stringify( + // { a: [{ b: { c: [1] } }] }, + // { allowDots: true, encode: false, arrayFormat: 'indices' }, + // ), + // 'a[0].b.c[0]=1', + // 'indices => indices', + // ); + // st.equal( + // stringify( + // { a: [{ b: { c: [1] } }] }, + // { allowDots: true, encode: false, arrayFormat: 'brackets' }, + // ), + // 'a[].b.c[]=1', + // 'brackets => brackets', + // ); + // st.equal( + // stringify({ a: [{ b: { c: [1] } }] }, { allowDots: true, encode: false }), + // 'a[0].b.c[0]=1', + // 'default => indices', + // ); + expect( + stringify({ a: [{ b: { c: [1] } }] }, { allowDots: true, encode: false, arrayFormat: 'indices' }), + ).toBe('a[0].b.c[0]=1'); + expect( + stringify({ a: [{ b: { c: [1] } }] }, { allowDots: true, encode: false, arrayFormat: 'brackets' }), + ).toBe('a[].b.c[]=1'); + expect(stringify({ a: [{ b: { c: [1] } }] }, { allowDots: true, encode: false })).toBe('a[0].b.c[0]=1'); + }); + + test('does not omit object keys when indices = false', function () { + // st.equal(stringify({ a: [{ b: 'c' }] }, { indices: false }), 'a%5Bb%5D=c'); + expect(stringify({ a: [{ b: 'c' }] }, { indices: false })).toBe('a%5Bb%5D=c'); + }); + + test('uses indices notation for arrays when indices=true', function () { + // st.equal(stringify({ a: ['b', 'c'] }, { indices: true }), 'a%5B0%5D=b&a%5B1%5D=c'); + expect(stringify({ a: ['b', 'c'] }, { indices: true })).toBe('a%5B0%5D=b&a%5B1%5D=c'); + }); + + test('uses indices notation for arrays when no arrayFormat is specified', function () { + // st.equal(stringify({ a: ['b', 'c'] }), 'a%5B0%5D=b&a%5B1%5D=c'); + expect(stringify({ a: ['b', 'c'] })).toBe('a%5B0%5D=b&a%5B1%5D=c'); + }); + + test('uses indices notation for arrays when arrayFormat=indices', function () { + // st.equal(stringify({ a: ['b', 'c'] }, { arrayFormat: 'indices' }), 'a%5B0%5D=b&a%5B1%5D=c'); + expect(stringify({ a: ['b', 'c'] }, { arrayFormat: 'indices' })).toBe('a%5B0%5D=b&a%5B1%5D=c'); + }); + + test('uses repeat notation for arrays when arrayFormat=repeat', function () { + // st.equal(stringify({ a: ['b', 'c'] }, { arrayFormat: 'repeat' }), 'a=b&a=c'); + expect(stringify({ a: ['b', 'c'] }, { arrayFormat: 'repeat' })).toBe('a=b&a=c'); + }); + + test('uses brackets notation for arrays when arrayFormat=brackets', function () { + // st.equal(stringify({ a: ['b', 'c'] }, { arrayFormat: 'brackets' }), 'a%5B%5D=b&a%5B%5D=c'); + expect(stringify({ a: ['b', 'c'] }, { arrayFormat: 'brackets' })).toBe('a%5B%5D=b&a%5B%5D=c'); + }); + + test('stringifies a complicated object', function () { + // st.equal(stringify({ a: { b: 'c', d: 'e' } }), 'a%5Bb%5D=c&a%5Bd%5D=e'); + expect(stringify({ a: { b: 'c', d: 'e' } })).toBe('a%5Bb%5D=c&a%5Bd%5D=e'); + }); + + test('stringifies an empty value', function () { + // st.equal(stringify({ a: '' }), 'a='); + // st.equal(stringify({ a: null }, { strictNullHandling: true }), 'a'); + expect(stringify({ a: '' })).toBe('a='); + expect(stringify({ a: null }, { strictNullHandling: true })).toBe('a'); + + // st.equal(stringify({ a: '', b: '' }), 'a=&b='); + // st.equal(stringify({ a: null, b: '' }, { strictNullHandling: true }), 'a&b='); + expect(stringify({ a: '', b: '' })).toBe('a=&b='); + expect(stringify({ a: null, b: '' }, { strictNullHandling: true })).toBe('a&b='); + + // st.equal(stringify({ a: { b: '' } }), 'a%5Bb%5D='); + // st.equal(stringify({ a: { b: null } }, { strictNullHandling: true }), 'a%5Bb%5D'); + // st.equal(stringify({ a: { b: null } }, { strictNullHandling: false }), 'a%5Bb%5D='); + expect(stringify({ a: { b: '' } })).toBe('a%5Bb%5D='); + expect(stringify({ a: { b: null } }, { strictNullHandling: true })).toBe('a%5Bb%5D'); + expect(stringify({ a: { b: null } }, { strictNullHandling: false })).toBe('a%5Bb%5D='); + }); + + test('stringifies an empty array in different arrayFormat', function () { + // st.equal(stringify({ a: [], b: [null], c: 'c' }, { encode: false }), 'b[0]=&c=c'); + expect(stringify({ a: [], b: [null], c: 'c' }, { encode: false })).toBe('b[0]=&c=c'); + // arrayFormat default + // st.equal( + // stringify({ a: [], b: [null], c: 'c' }, { encode: false, arrayFormat: 'indices' }), + // 'b[0]=&c=c', + // ); + // st.equal( + // stringify({ a: [], b: [null], c: 'c' }, { encode: false, arrayFormat: 'brackets' }), + // 'b[]=&c=c', + // ); + // st.equal( + // stringify({ a: [], b: [null], c: 'c' }, { encode: false, arrayFormat: 'repeat' }), + // 'b=&c=c', + // ); + // st.equal( + // stringify({ a: [], b: [null], c: 'c' }, { encode: false, arrayFormat: 'comma' }), + // 'b=&c=c', + // ); + // st.equal( + // stringify( + // { a: [], b: [null], c: 'c' }, + // { encode: false, arrayFormat: 'comma', commaRoundTrip: true }, + // ), + // 'b[]=&c=c', + // ); + expect(stringify({ a: [], b: [null], c: 'c' }, { encode: false, arrayFormat: 'indices' })).toBe( + 'b[0]=&c=c', + ); + expect(stringify({ a: [], b: [null], c: 'c' }, { encode: false, arrayFormat: 'brackets' })).toBe( + 'b[]=&c=c', + ); + expect(stringify({ a: [], b: [null], c: 'c' }, { encode: false, arrayFormat: 'repeat' })).toBe('b=&c=c'); + expect(stringify({ a: [], b: [null], c: 'c' }, { encode: false, arrayFormat: 'comma' })).toBe('b=&c=c'); + expect( + stringify({ a: [], b: [null], c: 'c' }, { encode: false, arrayFormat: 'comma', commaRoundTrip: true }), + ).toBe('b[]=&c=c'); + + // with strictNullHandling + // st.equal( + // stringify( + // { a: [], b: [null], c: 'c' }, + // { encode: false, arrayFormat: 'indices', strictNullHandling: true }, + // ), + // 'b[0]&c=c', + // ); + // st.equal( + // stringify( + // { a: [], b: [null], c: 'c' }, + // { encode: false, arrayFormat: 'brackets', strictNullHandling: true }, + // ), + // 'b[]&c=c', + // ); + // st.equal( + // stringify( + // { a: [], b: [null], c: 'c' }, + // { encode: false, arrayFormat: 'repeat', strictNullHandling: true }, + // ), + // 'b&c=c', + // ); + // st.equal( + // stringify( + // { a: [], b: [null], c: 'c' }, + // { encode: false, arrayFormat: 'comma', strictNullHandling: true }, + // ), + // 'b&c=c', + // ); + // st.equal( + // stringify( + // { a: [], b: [null], c: 'c' }, + // { encode: false, arrayFormat: 'comma', strictNullHandling: true, commaRoundTrip: true }, + // ), + // 'b[]&c=c', + // ); + + expect( + stringify( + { a: [], b: [null], c: 'c' }, + { encode: false, arrayFormat: 'indices', strictNullHandling: true }, + ), + ).toBe('b[0]&c=c'); + expect( + stringify( + { a: [], b: [null], c: 'c' }, + { encode: false, arrayFormat: 'brackets', strictNullHandling: true }, + ), + ).toBe('b[]&c=c'); + expect( + stringify( + { a: [], b: [null], c: 'c' }, + { encode: false, arrayFormat: 'repeat', strictNullHandling: true }, + ), + ).toBe('b&c=c'); + expect( + stringify( + { a: [], b: [null], c: 'c' }, + { encode: false, arrayFormat: 'comma', strictNullHandling: true }, + ), + ).toBe('b&c=c'); + expect( + stringify( + { a: [], b: [null], c: 'c' }, + { encode: false, arrayFormat: 'comma', strictNullHandling: true, commaRoundTrip: true }, + ), + ).toBe('b[]&c=c'); + + // with skipNulls + // st.equal( + // stringify( + // { a: [], b: [null], c: 'c' }, + // { encode: false, arrayFormat: 'indices', skipNulls: true }, + // ), + // 'c=c', + // ); + // st.equal( + // stringify( + // { a: [], b: [null], c: 'c' }, + // { encode: false, arrayFormat: 'brackets', skipNulls: true }, + // ), + // 'c=c', + // ); + // st.equal( + // stringify( + // { a: [], b: [null], c: 'c' }, + // { encode: false, arrayFormat: 'repeat', skipNulls: true }, + // ), + // 'c=c', + // ); + // st.equal( + // stringify( + // { a: [], b: [null], c: 'c' }, + // { encode: false, arrayFormat: 'comma', skipNulls: true }, + // ), + // 'c=c', + // ); + expect( + stringify({ a: [], b: [null], c: 'c' }, { encode: false, arrayFormat: 'indices', skipNulls: true }), + ).toBe('c=c'); + expect( + stringify({ a: [], b: [null], c: 'c' }, { encode: false, arrayFormat: 'brackets', skipNulls: true }), + ).toBe('c=c'); + expect( + stringify({ a: [], b: [null], c: 'c' }, { encode: false, arrayFormat: 'repeat', skipNulls: true }), + ).toBe('c=c'); + expect( + stringify({ a: [], b: [null], c: 'c' }, { encode: false, arrayFormat: 'comma', skipNulls: true }), + ).toBe('c=c'); + }); + + test('stringifies a null object', function () { + var obj = Object.create(null); + obj.a = 'b'; + // st.equal(stringify(obj), 'a=b'); + expect(stringify(obj)).toBe('a=b'); + }); + + test('returns an empty string for invalid input', function () { + // st.equal(stringify(undefined), ''); + // st.equal(stringify(false), ''); + // st.equal(stringify(null), ''); + // st.equal(stringify(''), ''); + expect(stringify(undefined)).toBe(''); + expect(stringify(false)).toBe(''); + expect(stringify(null)).toBe(''); + expect(stringify('')).toBe(''); + }); + + test('stringifies an object with a null object as a child', function () { + var obj = { a: Object.create(null) }; + + obj.a.b = 'c'; + // st.equal(stringify(obj), 'a%5Bb%5D=c'); + expect(stringify(obj)).toBe('a%5Bb%5D=c'); + }); + + test('drops keys with a value of undefined', function () { + // st.equal(stringify({ a: undefined }), ''); + expect(stringify({ a: undefined })).toBe(''); + + // st.equal( + // stringify({ a: { b: undefined, c: null } }, { strictNullHandling: true }), + // 'a%5Bc%5D', + // ); + // st.equal( + // stringify({ a: { b: undefined, c: null } }, { strictNullHandling: false }), + // 'a%5Bc%5D=', + // ); + // st.equal(stringify({ a: { b: undefined, c: '' } }), 'a%5Bc%5D='); + expect(stringify({ a: { b: undefined, c: null } }, { strictNullHandling: true })).toBe('a%5Bc%5D'); + expect(stringify({ a: { b: undefined, c: null } }, { strictNullHandling: false })).toBe('a%5Bc%5D='); + expect(stringify({ a: { b: undefined, c: '' } })).toBe('a%5Bc%5D='); + }); + + test('url encodes values', function () { + // st.equal(stringify({ a: 'b c' }), 'a=b%20c'); + expect(stringify({ a: 'b c' })).toBe('a=b%20c'); + }); + + test('stringifies a date', function () { + var now = new Date(); + var str = 'a=' + encodeURIComponent(now.toISOString()); + // st.equal(stringify({ a: now }), str); + expect(stringify({ a: now })).toBe(str); + }); + + test('stringifies the weird object from qs', function () { + // st.equal( + // stringify({ 'my weird field': '~q1!2"\'w$5&7/z8)?' }), + // 'my%20weird%20field=~q1%212%22%27w%245%267%2Fz8%29%3F', + // ); + expect(stringify({ 'my weird field': '~q1!2"\'w$5&7/z8)?' })).toBe( + 'my%20weird%20field=~q1%212%22%27w%245%267%2Fz8%29%3F', + ); + }); + + // TODO: Investigate how to to intercept in vitest + // TODO(rob) + test('skips properties that are part of the object prototype', function () { + // st.intercept(Object.prototype, 'crash', { value: 'test' }); + // @ts-expect-error + Object.prototype.crash = 'test'; + + // st.equal(stringify({ a: 'b' }), 'a=b'); + // st.equal(stringify({ a: { b: 'c' } }), 'a%5Bb%5D=c'); + expect(stringify({ a: 'b' })).toBe('a=b'); + expect(stringify({ a: { b: 'c' } })).toBe('a%5Bb%5D=c'); + }); + + test('stringifies boolean values', function () { + // st.equal(stringify({ a: true }), 'a=true'); + // st.equal(stringify({ a: { b: true } }), 'a%5Bb%5D=true'); + // st.equal(stringify({ b: false }), 'b=false'); + // st.equal(stringify({ b: { c: false } }), 'b%5Bc%5D=false'); + expect(stringify({ a: true })).toBe('a=true'); + expect(stringify({ a: { b: true } })).toBe('a%5Bb%5D=true'); + expect(stringify({ b: false })).toBe('b=false'); + expect(stringify({ b: { c: false } })).toBe('b%5Bc%5D=false'); + }); + + test('stringifies buffer values', function () { + // st.equal(stringify({ a: Buffer.from('test') }), 'a=test'); + // st.equal(stringify({ a: { b: Buffer.from('test') } }), 'a%5Bb%5D=test'); + }); + + test('stringifies an object using an alternative delimiter', function () { + // st.equal(stringify({ a: 'b', c: 'd' }, { delimiter: ';' }), 'a=b;c=d'); + expect(stringify({ a: 'b', c: 'd' }, { delimiter: ';' })).toBe('a=b;c=d'); + }); + + // We dont target environments which dont even have Buffer + // test('does not blow up when Buffer global is missing', function () { + // var restore = mockProperty(global, 'Buffer', { delete: true }); + + // var result = stringify({ a: 'b', c: 'd' }); + + // restore(); + + // st.equal(result, 'a=b&c=d'); + // st.end(); + // }); + + test('does not crash when parsing circular references', function () { + var a: any = {}; + a.b = a; + + // st['throws']( + // function () { + // stringify({ 'foo[bar]': 'baz', 'foo[baz]': a }); + // }, + // /RangeError: Cyclic object value/, + // 'cyclic values throw', + // ); + expect(() => { + stringify({ 'foo[bar]': 'baz', 'foo[baz]': a }); + }).toThrow('Cyclic object value'); + + var circular: any = { + a: 'value', + }; + circular.a = circular; + // st['throws']( + // function () { + // stringify(circular); + // }, + // /RangeError: Cyclic object value/, + // 'cyclic values throw', + // ); + expect(() => { + stringify(circular); + }).toThrow('Cyclic object value'); + + var arr = ['a']; + // st.doesNotThrow(function () { + // stringify({ x: arr, y: arr }); + // }, 'non-cyclic values do not throw'); + expect(() => { + stringify({ x: arr, y: arr }); + }).not.toThrow(); + }); + + test('non-circular duplicated references can still work', function () { + var hourOfDay = { + function: 'hour_of_day', + }; + + var p1 = { + function: 'gte', + arguments: [hourOfDay, 0], + }; + var p2 = { + function: 'lte', + arguments: [hourOfDay, 23], + }; + + // st.equal( + // stringify( + // { filters: { $and: [p1, p2] } }, + // { encodeValuesOnly: true, arrayFormat: 'indices' }, + // ), + // 'filters[$and][0][function]=gte&filters[$and][0][arguments][0][function]=hour_of_day&filters[$and][0][arguments][1]=0&filters[$and][1][function]=lte&filters[$and][1][arguments][0][function]=hour_of_day&filters[$and][1][arguments][1]=23', + // ); + // st.equal( + // stringify( + // { filters: { $and: [p1, p2] } }, + // { encodeValuesOnly: true, arrayFormat: 'brackets' }, + // ), + // 'filters[$and][][function]=gte&filters[$and][][arguments][][function]=hour_of_day&filters[$and][][arguments][]=0&filters[$and][][function]=lte&filters[$and][][arguments][][function]=hour_of_day&filters[$and][][arguments][]=23', + // ); + // st.equal( + // stringify( + // { filters: { $and: [p1, p2] } }, + // { encodeValuesOnly: true, arrayFormat: 'repeat' }, + // ), + // 'filters[$and][function]=gte&filters[$and][arguments][function]=hour_of_day&filters[$and][arguments]=0&filters[$and][function]=lte&filters[$and][arguments][function]=hour_of_day&filters[$and][arguments]=23', + // ); + expect( + stringify({ filters: { $and: [p1, p2] } }, { encodeValuesOnly: true, arrayFormat: 'indices' }), + ).toBe( + 'filters[$and][0][function]=gte&filters[$and][0][arguments][0][function]=hour_of_day&filters[$and][0][arguments][1]=0&filters[$and][1][function]=lte&filters[$and][1][arguments][0][function]=hour_of_day&filters[$and][1][arguments][1]=23', + ); + expect( + stringify({ filters: { $and: [p1, p2] } }, { encodeValuesOnly: true, arrayFormat: 'brackets' }), + ).toBe( + 'filters[$and][][function]=gte&filters[$and][][arguments][][function]=hour_of_day&filters[$and][][arguments][]=0&filters[$and][][function]=lte&filters[$and][][arguments][][function]=hour_of_day&filters[$and][][arguments][]=23', + ); + expect( + stringify({ filters: { $and: [p1, p2] } }, { encodeValuesOnly: true, arrayFormat: 'repeat' }), + ).toBe( + 'filters[$and][function]=gte&filters[$and][arguments][function]=hour_of_day&filters[$and][arguments]=0&filters[$and][function]=lte&filters[$and][arguments][function]=hour_of_day&filters[$and][arguments]=23', + ); + }); + + test('selects properties when filter=array', function () { + // st.equal(stringify({ a: 'b' }, { filter: ['a'] }), 'a=b'); + // st.equal(stringify({ a: 1 }, { filter: [] }), ''); + expect(stringify({ a: 'b' }, { filter: ['a'] })).toBe('a=b'); + expect(stringify({ a: 1 }, { filter: [] })).toBe(''); + + // st.equal( + // stringify( + // { a: { b: [1, 2, 3, 4], c: 'd' }, c: 'f' }, + // { filter: ['a', 'b', 0, 2], arrayFormat: 'indices' }, + // ), + // 'a%5Bb%5D%5B0%5D=1&a%5Bb%5D%5B2%5D=3', + // 'indices => indices', + // ); + // st.equal( + // stringify( + // { a: { b: [1, 2, 3, 4], c: 'd' }, c: 'f' }, + // { filter: ['a', 'b', 0, 2], arrayFormat: 'brackets' }, + // ), + // 'a%5Bb%5D%5B%5D=1&a%5Bb%5D%5B%5D=3', + // 'brackets => brackets', + // ); + // st.equal( + // stringify({ a: { b: [1, 2, 3, 4], c: 'd' }, c: 'f' }, { filter: ['a', 'b', 0, 2] }), + // 'a%5Bb%5D%5B0%5D=1&a%5Bb%5D%5B2%5D=3', + // 'default => indices', + // ); + expect(stringify({ a: { b: [1, 2, 3, 4], c: 'd' }, c: 'f' }, { filter: ['a', 'b', 0, 2] })).toBe( + 'a%5Bb%5D%5B0%5D=1&a%5Bb%5D%5B2%5D=3', + ); + expect( + stringify( + { a: { b: [1, 2, 3, 4], c: 'd' }, c: 'f' }, + { filter: ['a', 'b', 0, 2], arrayFormat: 'indices' }, + ), + ).toBe('a%5Bb%5D%5B0%5D=1&a%5Bb%5D%5B2%5D=3'); + expect( + stringify( + { a: { b: [1, 2, 3, 4], c: 'd' }, c: 'f' }, + { filter: ['a', 'b', 0, 2], arrayFormat: 'brackets' }, + ), + ).toBe('a%5Bb%5D%5B%5D=1&a%5Bb%5D%5B%5D=3'); + }); + + test('supports custom representations when filter=function', function () { + var calls = 0; + var obj = { a: 'b', c: 'd', e: { f: new Date(1257894000000) } }; + var filterFunc: StringifyOptions['filter'] = function (prefix, value) { + calls += 1; + if (calls === 1) { + // st.equal(prefix, '', 'prefix is empty'); + // st.equal(value, obj); + expect(prefix).toBe(''); + expect(value).toBe(obj); + } else if (prefix === 'c') { + return void 0; + } else if (value instanceof Date) { + // st.equal(prefix, 'e[f]'); + expect(prefix).toBe('e[f]'); + return value.getTime(); + } + return value; + }; + + // st.equal(stringify(obj, { filter: filterFunc }), 'a=b&e%5Bf%5D=1257894000000'); + // st.equal(calls, 5); + expect(stringify(obj, { filter: filterFunc })).toBe('a=b&e%5Bf%5D=1257894000000'); + expect(calls).toBe(5); + }); + + test('can disable uri encoding', function () { + // st.equal(stringify({ a: 'b' }, { encode: false }), 'a=b'); + // st.equal(stringify({ a: { b: 'c' } }, { encode: false }), 'a[b]=c'); + // st.equal( + // stringify({ a: 'b', c: null }, { strictNullHandling: true, encode: false }), + // 'a=b&c', + // ); + expect(stringify({ a: 'b' }, { encode: false })).toBe('a=b'); + expect(stringify({ a: { b: 'c' } }, { encode: false })).toBe('a[b]=c'); + expect(stringify({ a: 'b', c: null }, { strictNullHandling: true, encode: false })).toBe('a=b&c'); + }); + + test('can sort the keys', function () { + // @ts-expect-error + var sort: NonNullable = function (a: string, b: string) { + return a.localeCompare(b); + }; + // st.equal(stringify({ a: 'c', z: 'y', b: 'f' }, { sort: sort }), 'a=c&b=f&z=y'); + // st.equal( + // stringify({ a: 'c', z: { j: 'a', i: 'b' }, b: 'f' }, { sort: sort }), + // 'a=c&b=f&z%5Bi%5D=b&z%5Bj%5D=a', + // ); + expect(stringify({ a: 'c', z: 'y', b: 'f' }, { sort: sort })).toBe('a=c&b=f&z=y'); + expect(stringify({ a: 'c', z: { j: 'a', i: 'b' }, b: 'f' }, { sort: sort })).toBe( + 'a=c&b=f&z%5Bi%5D=b&z%5Bj%5D=a', + ); + }); + + test('can sort the keys at depth 3 or more too', function () { + // @ts-expect-error + var sort: NonNullable = function (a: string, b: string) { + return a.localeCompare(b); + }; + // st.equal( + // stringify( + // { a: 'a', z: { zj: { zjb: 'zjb', zja: 'zja' }, zi: { zib: 'zib', zia: 'zia' } }, b: 'b' }, + // { sort: sort, encode: false }, + // ), + // 'a=a&b=b&z[zi][zia]=zia&z[zi][zib]=zib&z[zj][zja]=zja&z[zj][zjb]=zjb', + // ); + // st.equal( + // stringify( + // { a: 'a', z: { zj: { zjb: 'zjb', zja: 'zja' }, zi: { zib: 'zib', zia: 'zia' } }, b: 'b' }, + // { sort: null, encode: false }, + // ), + // 'a=a&z[zj][zjb]=zjb&z[zj][zja]=zja&z[zi][zib]=zib&z[zi][zia]=zia&b=b', + // ); + expect( + stringify( + { a: 'a', z: { zj: { zjb: 'zjb', zja: 'zja' }, zi: { zib: 'zib', zia: 'zia' } }, b: 'b' }, + { sort: sort, encode: false }, + ), + ).toBe('a=a&b=b&z[zi][zia]=zia&z[zi][zib]=zib&z[zj][zja]=zja&z[zj][zjb]=zjb'); + expect( + stringify( + { a: 'a', z: { zj: { zjb: 'zjb', zja: 'zja' }, zi: { zib: 'zib', zia: 'zia' } }, b: 'b' }, + { sort: null, encode: false }, + ), + ).toBe('a=a&z[zj][zjb]=zjb&z[zj][zja]=zja&z[zi][zib]=zib&z[zi][zia]=zia&b=b'); + }); + + test('can stringify with custom encoding', function () { + // st.equal( + // stringify( + // { 県: '大阪府', '': '' }, + // { + // encoder: function (str) { + // if (str.length === 0) { + // return ''; + // } + // var buf = iconv.encode(str, 'shiftjis'); + // var result = []; + // for (var i = 0; i < buf.length; ++i) { + // result.push(buf.readUInt8(i).toString(16)); + // } + // return '%' + result.join('%'); + // }, + // }, + // ), + // '%8c%a7=%91%e5%8d%e3%95%7b&=', + // ); + expect( + stringify( + { 県: '大阪府', '': '' }, + { + encoder: function (str) { + if (str.length === 0) { + return ''; + } + var buf = iconv.encode(str, 'shiftjis'); + var result = []; + for (var i = 0; i < buf.length; ++i) { + result.push(buf.readUInt8(i).toString(16)); + } + return '%' + result.join('%'); + }, + }, + ), + ).toBe('%8c%a7=%91%e5%8d%e3%95%7b&='); + }); + + test('receives the default encoder as a second argument', function () { + // stringify( + // { a: 1, b: new Date(), c: true, d: [1] }, + // { + // encoder: function (str) { + // st.match(typeof str, /^(?:string|number|boolean)$/); + // return ''; + // }, + // }, + // ); + + stringify( + { a: 1, b: new Date(), c: true, d: [1] }, + { + encoder: function (str) { + // st.match(typeof str, /^(?:string|number|boolean)$/); + assert.match(typeof str, /^(?:string|number|boolean)$/); + return ''; + }, + }, + ); + }); + + test('receives the default encoder as a second argument', function () { + // stringify( + // { a: 1 }, + // { + // encoder: function (str, defaultEncoder) { + // st.equal(defaultEncoder, utils.encode); + // }, + // }, + // ); + + stringify( + { a: 1 }, + { + // @ts-ignore + encoder: function (_str, defaultEncoder) { + expect(defaultEncoder).toBe(encode); + }, + }, + ); + }); + + test('throws error with wrong encoder', function () { + // st['throws'](function () { + // stringify({}, { encoder: 'string' }); + // }, new TypeError('Encoder has to be a function.')); + // st.end(); + expect(() => { + // @ts-expect-error + stringify({}, { encoder: 'string' }); + }).toThrow(TypeError); + }); + + (typeof Buffer === 'undefined' ? test.skip : test)( + 'can use custom encoder for a buffer object', + function () { + // st.equal( + // stringify( + // { a: Buffer.from([1]) }, + // { + // encoder: function (buffer) { + // if (typeof buffer === 'string') { + // return buffer; + // } + // return String.fromCharCode(buffer.readUInt8(0) + 97); + // }, + // }, + // ), + // 'a=b', + // ); + expect( + stringify( + { a: Buffer.from([1]) }, + { + encoder: function (buffer) { + if (typeof buffer === 'string') { + return buffer; + } + return String.fromCharCode(buffer.readUInt8(0) + 97); + }, + }, + ), + ).toBe('a=b'); + + // st.equal( + // stringify( + // { a: Buffer.from('a b') }, + // { + // encoder: function (buffer) { + // return buffer; + // }, + // }, + // ), + // 'a=a b', + // ); + expect( + stringify( + { a: Buffer.from('a b') }, + { + encoder: function (buffer) { + return buffer; + }, + }, + ), + ).toBe('a=a b'); + }, + ); + + test('serializeDate option', function () { + var date = new Date(); + // st.equal( + // stringify({ a: date }), + // 'a=' + date.toISOString().replace(/:/g, '%3A'), + // 'default is toISOString', + // ); + expect(stringify({ a: date })).toBe('a=' + date.toISOString().replace(/:/g, '%3A')); + + var mutatedDate = new Date(); + mutatedDate.toISOString = function () { + throw new SyntaxError(); + }; + // st['throws'](function () { + // mutatedDate.toISOString(); + // }, SyntaxError); + expect(() => { + mutatedDate.toISOString(); + }).toThrow(SyntaxError); + // st.equal( + // stringify({ a: mutatedDate }), + // 'a=' + Date.prototype.toISOString.call(mutatedDate).replace(/:/g, '%3A'), + // 'toISOString works even when method is not locally present', + // ); + expect(stringify({ a: mutatedDate })).toBe( + 'a=' + Date.prototype.toISOString.call(mutatedDate).replace(/:/g, '%3A'), + ); + + var specificDate = new Date(6); + // st.equal( + // stringify( + // { a: specificDate }, + // { + // serializeDate: function (d) { + // return d.getTime() * 7; + // }, + // }, + // ), + // 'a=42', + // 'custom serializeDate function called', + // ); + expect( + stringify( + { a: specificDate }, + { + // @ts-ignore + serializeDate: function (d) { + return d.getTime() * 7; + }, + }, + ), + ).toBe('a=42'); + + // st.equal( + // stringify( + // { a: [date] }, + // { + // serializeDate: function (d) { + // return d.getTime(); + // }, + // arrayFormat: 'comma', + // }, + // ), + // 'a=' + date.getTime(), + // 'works with arrayFormat comma', + // ); + // st.equal( + // stringify( + // { a: [date] }, + // { + // serializeDate: function (d) { + // return d.getTime(); + // }, + // arrayFormat: 'comma', + // commaRoundTrip: true, + // }, + // ), + // 'a%5B%5D=' + date.getTime(), + // 'works with arrayFormat comma', + // ); + expect( + stringify( + { a: [date] }, + { + // @ts-expect-error + serializeDate: function (d) { + return d.getTime(); + }, + arrayFormat: 'comma', + }, + ), + ).toBe('a=' + date.getTime()); + expect( + stringify( + { a: [date] }, + { + // @ts-expect-error + serializeDate: function (d) { + return d.getTime(); + }, + arrayFormat: 'comma', + commaRoundTrip: true, + }, + ), + ).toBe('a%5B%5D=' + date.getTime()); + }); + + test('RFC 1738 serialization', function () { + // st.equal(stringify({ a: 'b c' }, { format: formats.RFC1738 }), 'a=b+c'); + // st.equal(stringify({ 'a b': 'c d' }, { format: formats.RFC1738 }), 'a+b=c+d'); + // st.equal( + // stringify({ 'a b': Buffer.from('a b') }, { format: formats.RFC1738 }), + // 'a+b=a+b', + // ); + expect(stringify({ a: 'b c' }, { format: 'RFC1738' })).toBe('a=b+c'); + expect(stringify({ 'a b': 'c d' }, { format: 'RFC1738' })).toBe('a+b=c+d'); + expect(stringify({ 'a b': Buffer.from('a b') }, { format: 'RFC1738' })).toBe('a+b=a+b'); + + // st.equal(stringify({ 'foo(ref)': 'bar' }, { format: formats.RFC1738 }), 'foo(ref)=bar'); + expect(stringify({ 'foo(ref)': 'bar' }, { format: 'RFC1738' })).toBe('foo(ref)=bar'); + }); + + test('RFC 3986 spaces serialization', function () { + // st.equal(stringify({ a: 'b c' }, { format: formats.RFC3986 }), 'a=b%20c'); + // st.equal(stringify({ 'a b': 'c d' }, { format: formats.RFC3986 }), 'a%20b=c%20d'); + // st.equal( + // stringify({ 'a b': Buffer.from('a b') }, { format: formats.RFC3986 }), + // 'a%20b=a%20b', + // ); + expect(stringify({ a: 'b c' }, { format: 'RFC3986' })).toBe('a=b%20c'); + expect(stringify({ 'a b': 'c d' }, { format: 'RFC3986' })).toBe('a%20b=c%20d'); + expect(stringify({ 'a b': Buffer.from('a b') }, { format: 'RFC3986' })).toBe('a%20b=a%20b'); + }); + + test('Backward compatibility to RFC 3986', function () { + // st.equal(stringify({ a: 'b c' }), 'a=b%20c'); + // st.equal(stringify({ 'a b': Buffer.from('a b') }), 'a%20b=a%20b'); + expect(stringify({ a: 'b c' })).toBe('a=b%20c'); + expect(stringify({ 'a b': Buffer.from('a b') })).toBe('a%20b=a%20b'); + }); + + test('Edge cases and unknown formats', function () { + ['UFO1234', false, 1234, null, {}, []].forEach(function (format) { + // st['throws'](function () { + // stringify({ a: 'b c' }, { format: format }); + // }, new TypeError('Unknown format option provided.')); + expect(() => { + // @ts-expect-error + stringify({ a: 'b c' }, { format: format }); + }).toThrow(TypeError); + }); + }); + + test('encodeValuesOnly', function () { + // st.equal( + // stringify( + // { a: 'b', c: ['d', 'e=f'], f: [['g'], ['h']] }, + // { encodeValuesOnly: true, arrayFormat: 'indices' }, + // ), + // 'a=b&c[0]=d&c[1]=e%3Df&f[0][0]=g&f[1][0]=h', + // 'encodeValuesOnly + indices', + // ); + // st.equal( + // stringify( + // { a: 'b', c: ['d', 'e=f'], f: [['g'], ['h']] }, + // { encodeValuesOnly: true, arrayFormat: 'brackets' }, + // ), + // 'a=b&c[]=d&c[]=e%3Df&f[][]=g&f[][]=h', + // 'encodeValuesOnly + brackets', + // ); + // st.equal( + // stringify( + // { a: 'b', c: ['d', 'e=f'], f: [['g'], ['h']] }, + // { encodeValuesOnly: true, arrayFormat: 'repeat' }, + // ), + // 'a=b&c=d&c=e%3Df&f=g&f=h', + // 'encodeValuesOnly + repeat', + // ); + expect( + stringify( + { a: 'b', c: ['d', 'e=f'], f: [['g'], ['h']] }, + { encodeValuesOnly: true, arrayFormat: 'indices' }, + ), + ).toBe('a=b&c[0]=d&c[1]=e%3Df&f[0][0]=g&f[1][0]=h'); + expect( + stringify( + { a: 'b', c: ['d', 'e=f'], f: [['g'], ['h']] }, + { encodeValuesOnly: true, arrayFormat: 'brackets' }, + ), + ).toBe('a=b&c[]=d&c[]=e%3Df&f[][]=g&f[][]=h'); + expect( + stringify( + { a: 'b', c: ['d', 'e=f'], f: [['g'], ['h']] }, + { encodeValuesOnly: true, arrayFormat: 'repeat' }, + ), + ).toBe('a=b&c=d&c=e%3Df&f=g&f=h'); + + // st.equal( + // stringify({ a: 'b', c: ['d', 'e'], f: [['g'], ['h']] }, { arrayFormat: 'indices' }), + // 'a=b&c%5B0%5D=d&c%5B1%5D=e&f%5B0%5D%5B0%5D=g&f%5B1%5D%5B0%5D=h', + // 'no encodeValuesOnly + indices', + // ); + // st.equal( + // stringify({ a: 'b', c: ['d', 'e'], f: [['g'], ['h']] }, { arrayFormat: 'brackets' }), + // 'a=b&c%5B%5D=d&c%5B%5D=e&f%5B%5D%5B%5D=g&f%5B%5D%5B%5D=h', + // 'no encodeValuesOnly + brackets', + // ); + // st.equal( + // stringify({ a: 'b', c: ['d', 'e'], f: [['g'], ['h']] }, { arrayFormat: 'repeat' }), + // 'a=b&c=d&c=e&f=g&f=h', + // 'no encodeValuesOnly + repeat', + // ); + expect(stringify({ a: 'b', c: ['d', 'e'], f: [['g'], ['h']] }, { arrayFormat: 'indices' })).toBe( + 'a=b&c%5B0%5D=d&c%5B1%5D=e&f%5B0%5D%5B0%5D=g&f%5B1%5D%5B0%5D=h', + ); + expect(stringify({ a: 'b', c: ['d', 'e'], f: [['g'], ['h']] }, { arrayFormat: 'brackets' })).toBe( + 'a=b&c%5B%5D=d&c%5B%5D=e&f%5B%5D%5B%5D=g&f%5B%5D%5B%5D=h', + ); + expect(stringify({ a: 'b', c: ['d', 'e'], f: [['g'], ['h']] }, { arrayFormat: 'repeat' })).toBe( + 'a=b&c=d&c=e&f=g&f=h', + ); + }); + + test('encodeValuesOnly - strictNullHandling', function () { + // st.equal( + // stringify({ a: { b: null } }, { encodeValuesOnly: true, strictNullHandling: true }), + // 'a[b]', + // ); + expect(stringify({ a: { b: null } }, { encodeValuesOnly: true, strictNullHandling: true })).toBe('a[b]'); + }); + + test('throws if an invalid charset is specified', function () { + // st['throws'](function () { + // stringify({ a: 'b' }, { charset: 'foobar' }); + // }, new TypeError('The charset option must be either utf-8, iso-8859-1, or undefined')); + expect(() => { + // @ts-expect-error + stringify({ a: 'b' }, { charset: 'foobar' }); + }).toThrow(TypeError); + }); + + test('respects a charset of iso-8859-1', function () { + // st.equal(stringify({ æ: 'æ' }, { charset: 'iso-8859-1' }), '%E6=%E6'); + expect(stringify({ æ: 'æ' }, { charset: 'iso-8859-1' })).toBe('%E6=%E6'); + }); + + test('encodes unrepresentable chars as numeric entities in iso-8859-1 mode', function () { + // st.equal(stringify({ a: '☺' }, { charset: 'iso-8859-1' }), 'a=%26%239786%3B'); + expect(stringify({ a: '☺' }, { charset: 'iso-8859-1' })).toBe('a=%26%239786%3B'); + }); + + test('respects an explicit charset of utf-8 (the default)', function () { + // st.equal(stringify({ a: 'æ' }, { charset: 'utf-8' }), 'a=%C3%A6'); + expect(stringify({ a: 'æ' }, { charset: 'utf-8' })).toBe('a=%C3%A6'); + }); + + test('`charsetSentinel` option', function () { + // st.equal( + // stringify({ a: 'æ' }, { charsetSentinel: true, charset: 'utf-8' }), + // 'utf8=%E2%9C%93&a=%C3%A6', + // 'adds the right sentinel when instructed to and the charset is utf-8', + // ); + expect(stringify({ a: 'æ' }, { charsetSentinel: true, charset: 'utf-8' })).toBe( + 'utf8=%E2%9C%93&a=%C3%A6', + ); + + // st.equal( + // stringify({ a: 'æ' }, { charsetSentinel: true, charset: 'iso-8859-1' }), + // 'utf8=%26%2310003%3B&a=%E6', + // 'adds the right sentinel when instructed to and the charset is iso-8859-1', + // ); + expect(stringify({ a: 'æ' }, { charsetSentinel: true, charset: 'iso-8859-1' })).toBe( + 'utf8=%26%2310003%3B&a=%E6', + ); + }); + + test('does not mutate the options argument', function () { + var options = {}; + stringify({}, options); + // st.deepEqual(options, {}); + expect(options).toEqual({}); + }); + + test('strictNullHandling works with custom filter', function () { + // @ts-expect-error + var filter = function (_prefix, value) { + return value; + }; + + var options = { strictNullHandling: true, filter: filter }; + // st.equal(stringify({ key: null }, options), 'key'); + expect(stringify({ key: null }, options)).toBe('key'); + }); + + test('strictNullHandling works with null serializeDate', function () { + var serializeDate = function () { + return null; + }; + var options = { strictNullHandling: true, serializeDate: serializeDate }; + var date = new Date(); + // st.equal(stringify({ key: date }, options), 'key'); + // @ts-expect-error + expect(stringify({ key: date }, options)).toBe('key'); + }); + + test('allows for encoding keys and values differently', function () { + // @ts-expect-error + var encoder = function (str, defaultEncoder, charset, type) { + if (type === 'key') { + return defaultEncoder(str, defaultEncoder, charset, type).toLowerCase(); + } + if (type === 'value') { + return defaultEncoder(str, defaultEncoder, charset, type).toUpperCase(); + } + throw 'this should never happen! type: ' + type; + }; + + // st.deepEqual(stringify({ KeY: 'vAlUe' }, { encoder: encoder }), 'key=VALUE'); + expect(stringify({ KeY: 'vAlUe' }, { encoder: encoder })).toBe('key=VALUE'); + }); + + test('objects inside arrays', function () { + var obj = { a: { b: { c: 'd', e: 'f' } } }; + var withArray = { a: { b: [{ c: 'd', e: 'f' }] } }; + + // st.equal( + // stringify(obj, { encode: false }), + // 'a[b][c]=d&a[b][e]=f', + // 'no array, no arrayFormat', + // ); + // st.equal( + // stringify(obj, { encode: false, arrayFormat: 'brackets' }), + // 'a[b][c]=d&a[b][e]=f', + // 'no array, bracket', + // ); + // st.equal( + // stringify(obj, { encode: false, arrayFormat: 'indices' }), + // 'a[b][c]=d&a[b][e]=f', + // 'no array, indices', + // ); + // st.equal( + // stringify(obj, { encode: false, arrayFormat: 'repeat' }), + // 'a[b][c]=d&a[b][e]=f', + // 'no array, repeat', + // ); + // st.equal( + // stringify(obj, { encode: false, arrayFormat: 'comma' }), + // 'a[b][c]=d&a[b][e]=f', + // 'no array, comma', + // ); + expect(stringify(obj, { encode: false })).toBe('a[b][c]=d&a[b][e]=f'); + expect(stringify(obj, { encode: false, arrayFormat: 'brackets' })).toBe('a[b][c]=d&a[b][e]=f'); + expect(stringify(obj, { encode: false, arrayFormat: 'indices' })).toBe('a[b][c]=d&a[b][e]=f'); + expect(stringify(obj, { encode: false, arrayFormat: 'repeat' })).toBe('a[b][c]=d&a[b][e]=f'); + expect(stringify(obj, { encode: false, arrayFormat: 'comma' })).toBe('a[b][c]=d&a[b][e]=f'); + + // st.equal( + // stringify(withArray, { encode: false }), + // 'a[b][0][c]=d&a[b][0][e]=f', + // 'array, no arrayFormat', + // ); + // st.equal( + // stringify(withArray, { encode: false, arrayFormat: 'brackets' }), + // 'a[b][][c]=d&a[b][][e]=f', + // 'array, bracket', + // ); + // st.equal( + // stringify(withArray, { encode: false, arrayFormat: 'indices' }), + // 'a[b][0][c]=d&a[b][0][e]=f', + // 'array, indices', + // ); + // st.equal( + // stringify(withArray, { encode: false, arrayFormat: 'repeat' }), + // 'a[b][c]=d&a[b][e]=f', + // 'array, repeat', + // ); + // st.equal( + // stringify(withArray, { encode: false, arrayFormat: 'comma' }), + // '???', + // 'array, comma', + // { skip: 'TODO: figure out what this should do' }, + // ); + expect(stringify(withArray, { encode: false })).toBe('a[b][0][c]=d&a[b][0][e]=f'); + expect(stringify(withArray, { encode: false, arrayFormat: 'brackets' })).toBe('a[b][][c]=d&a[b][][e]=f'); + expect(stringify(withArray, { encode: false, arrayFormat: 'indices' })).toBe('a[b][0][c]=d&a[b][0][e]=f'); + expect(stringify(withArray, { encode: false, arrayFormat: 'repeat' })).toBe('a[b][c]=d&a[b][e]=f'); + // !TODo: Figure out what this should do + // expect(stringify(withArray, { encode: false, arrayFormat: 'comma' })).toBe( + // 'a[b][c]=d&a[b][e]=f', + // ); + }); + + test('stringifies sparse arrays', function () { + // st.equal( + // stringify({ a: [, '2', , , '1'] }, { encodeValuesOnly: true, arrayFormat: 'indices' }), + // 'a[1]=2&a[4]=1', + // ); + // st.equal( + // stringify({ a: [, '2', , , '1'] }, { encodeValuesOnly: true, arrayFormat: 'brackets' }), + // 'a[]=2&a[]=1', + // ); + // st.equal( + // stringify({ a: [, '2', , , '1'] }, { encodeValuesOnly: true, arrayFormat: 'repeat' }), + // 'a=2&a=1', + // ); + expect(stringify({ a: [, '2', , , '1'] }, { encodeValuesOnly: true, arrayFormat: 'indices' })).toBe( + 'a[1]=2&a[4]=1', + ); + expect(stringify({ a: [, '2', , , '1'] }, { encodeValuesOnly: true, arrayFormat: 'brackets' })).toBe( + 'a[]=2&a[]=1', + ); + expect(stringify({ a: [, '2', , , '1'] }, { encodeValuesOnly: true, arrayFormat: 'repeat' })).toBe( + 'a=2&a=1', + ); + + // st.equal( + // stringify( + // { a: [, { b: [, , { c: '1' }] }] }, + // { encodeValuesOnly: true, arrayFormat: 'indices' }, + // ), + // 'a[1][b][2][c]=1', + // ); + // st.equal( + // stringify( + // { a: [, { b: [, , { c: '1' }] }] }, + // { encodeValuesOnly: true, arrayFormat: 'brackets' }, + // ), + // 'a[][b][][c]=1', + // ); + // st.equal( + // stringify( + // { a: [, { b: [, , { c: '1' }] }] }, + // { encodeValuesOnly: true, arrayFormat: 'repeat' }, + // ), + // 'a[b][c]=1', + // ); + expect( + stringify({ a: [, { b: [, , { c: '1' }] }] }, { encodeValuesOnly: true, arrayFormat: 'indices' }), + ).toBe('a[1][b][2][c]=1'); + expect( + stringify({ a: [, { b: [, , { c: '1' }] }] }, { encodeValuesOnly: true, arrayFormat: 'brackets' }), + ).toBe('a[][b][][c]=1'); + expect( + stringify({ a: [, { b: [, , { c: '1' }] }] }, { encodeValuesOnly: true, arrayFormat: 'repeat' }), + ).toBe('a[b][c]=1'); + + // st.equal( + // stringify( + // { a: [, [, , [, , , { c: '1' }]]] }, + // { encodeValuesOnly: true, arrayFormat: 'indices' }, + // ), + // 'a[1][2][3][c]=1', + // ); + // st.equal( + // stringify( + // { a: [, [, , [, , , { c: '1' }]]] }, + // { encodeValuesOnly: true, arrayFormat: 'brackets' }, + // ), + // 'a[][][][c]=1', + // ); + // st.equal( + // stringify( + // { a: [, [, , [, , , { c: '1' }]]] }, + // { encodeValuesOnly: true, arrayFormat: 'repeat' }, + // ), + // 'a[c]=1', + // ); + expect( + stringify({ a: [, [, , [, , , { c: '1' }]]] }, { encodeValuesOnly: true, arrayFormat: 'indices' }), + ).toBe('a[1][2][3][c]=1'); + expect( + stringify({ a: [, [, , [, , , { c: '1' }]]] }, { encodeValuesOnly: true, arrayFormat: 'brackets' }), + ).toBe('a[][][][c]=1'); + expect( + stringify({ a: [, [, , [, , , { c: '1' }]]] }, { encodeValuesOnly: true, arrayFormat: 'repeat' }), + ).toBe('a[c]=1'); + + // st.equal( + // stringify( + // { a: [, [, , [, , , { c: [, '1'] }]]] }, + // { encodeValuesOnly: true, arrayFormat: 'indices' }, + // ), + // 'a[1][2][3][c][1]=1', + // ); + // st.equal( + // stringify( + // { a: [, [, , [, , , { c: [, '1'] }]]] }, + // { encodeValuesOnly: true, arrayFormat: 'brackets' }, + // ), + // 'a[][][][c][]=1', + // ); + // st.equal( + // stringify( + // { a: [, [, , [, , , { c: [, '1'] }]]] }, + // { encodeValuesOnly: true, arrayFormat: 'repeat' }, + // ), + // 'a[c]=1', + // ); + expect( + stringify({ a: [, [, , [, , , { c: [, '1'] }]]] }, { encodeValuesOnly: true, arrayFormat: 'indices' }), + ).toBe('a[1][2][3][c][1]=1'); + expect( + stringify({ a: [, [, , [, , , { c: [, '1'] }]]] }, { encodeValuesOnly: true, arrayFormat: 'brackets' }), + ).toBe('a[][][][c][]=1'); + expect( + stringify({ a: [, [, , [, , , { c: [, '1'] }]]] }, { encodeValuesOnly: true, arrayFormat: 'repeat' }), + ).toBe('a[c]=1'); + }); + + test('encodes a very long string', function () { + var chars = []; + var expected = []; + for (var i = 0; i < 5e3; i++) { + chars.push(' ' + i); + + expected.push('%20' + i); + } + + var obj = { + foo: chars.join(''), + }; + + // st.equal( + // stringify(obj, { arrayFormat: 'bracket', charset: 'utf-8' }), + // 'foo=' + expected.join(''), + // ); + // @ts-expect-error + expect(stringify(obj, { arrayFormat: 'bracket', charset: 'utf-8' })).toBe('foo=' + expected.join('')); + }); +}); + +describe('stringifies empty keys', function () { + empty_test_cases.forEach(function (testCase) { + test('stringifies an object with empty string key with ' + testCase.input, function () { + // st.deepEqual( + // stringify(testCase.withEmptyKeys, { encode: false, arrayFormat: 'indices' }), + // testCase.stringifyOutput.indices, + // 'test case: ' + testCase.input + ', indices', + // ); + // st.deepEqual( + // stringify(testCase.withEmptyKeys, { encode: false, arrayFormat: 'brackets' }), + // testCase.stringifyOutput.brackets, + // 'test case: ' + testCase.input + ', brackets', + // ); + // st.deepEqual( + // stringify(testCase.withEmptyKeys, { encode: false, arrayFormat: 'repeat' }), + // testCase.stringifyOutput.repeat, + // 'test case: ' + testCase.input + ', repeat', + // ); + expect(stringify(testCase.with_empty_keys, { encode: false, arrayFormat: 'indices' })).toBe( + testCase.stringify_output.indices, + ); + expect(stringify(testCase.with_empty_keys, { encode: false, arrayFormat: 'brackets' })).toBe( + testCase.stringify_output.brackets, + ); + expect(stringify(testCase.with_empty_keys, { encode: false, arrayFormat: 'repeat' })).toBe( + testCase.stringify_output.repeat, + ); + }); + }); + + test('edge case with object/arrays', function () { + // st.deepEqual(stringify({ '': { '': [2, 3] } }, { encode: false }), '[][0]=2&[][1]=3'); + // st.deepEqual( + // stringify({ '': { '': [2, 3], a: 2 } }, { encode: false }), + // '[][0]=2&[][1]=3&[a]=2', + // ); + // st.deepEqual( + // stringify({ '': { '': [2, 3] } }, { encode: false, arrayFormat: 'indices' }), + // '[][0]=2&[][1]=3', + // ); + // st.deepEqual( + // stringify({ '': { '': [2, 3], a: 2 } }, { encode: false, arrayFormat: 'indices' }), + // '[][0]=2&[][1]=3&[a]=2', + // ); + expect(stringify({ '': { '': [2, 3] } }, { encode: false })).toBe('[][0]=2&[][1]=3'); + expect(stringify({ '': { '': [2, 3], a: 2 } }, { encode: false })).toBe('[][0]=2&[][1]=3&[a]=2'); + expect(stringify({ '': { '': [2, 3] } }, { encode: false, arrayFormat: 'indices' })).toBe( + '[][0]=2&[][1]=3', + ); + expect(stringify({ '': { '': [2, 3], a: 2 } }, { encode: false, arrayFormat: 'indices' })).toBe( + '[][0]=2&[][1]=3&[a]=2', + ); + }); +}); diff --git a/tests/qs/utils.test.ts b/tests/qs/utils.test.ts new file mode 100644 index 000000000..3df95e5bd --- /dev/null +++ b/tests/qs/utils.test.ts @@ -0,0 +1,169 @@ +import { combine, merge, is_buffer, assign_single_source } from 'openai/internal/qs/utils'; + +describe('merge()', function () { + // t.deepEqual(merge(null, true), [null, true], 'merges true into null'); + expect(merge(null, true)).toEqual([null, true]); + + // t.deepEqual(merge(null, [42]), [null, 42], 'merges null into an array'); + expect(merge(null, [42])).toEqual([null, 42]); + + // t.deepEqual( + // merge({ a: 'b' }, { a: 'c' }), + // { a: ['b', 'c'] }, + // 'merges two objects with the same key', + // ); + expect(merge({ a: 'b' }, { a: 'c' })).toEqual({ a: ['b', 'c'] }); + + var oneMerged = merge({ foo: 'bar' }, { foo: { first: '123' } }); + // t.deepEqual( + // oneMerged, + // { foo: ['bar', { first: '123' }] }, + // 'merges a standalone and an object into an array', + // ); + expect(oneMerged).toEqual({ foo: ['bar', { first: '123' }] }); + + var twoMerged = merge({ foo: ['bar', { first: '123' }] }, { foo: { second: '456' } }); + // t.deepEqual( + // twoMerged, + // { foo: { 0: 'bar', 1: { first: '123' }, second: '456' } }, + // 'merges a standalone and two objects into an array', + // ); + expect(twoMerged).toEqual({ foo: { 0: 'bar', 1: { first: '123' }, second: '456' } }); + + var sandwiched = merge({ foo: ['bar', { first: '123', second: '456' }] }, { foo: 'baz' }); + // t.deepEqual( + // sandwiched, + // { foo: ['bar', { first: '123', second: '456' }, 'baz'] }, + // 'merges an object sandwiched by two standalones into an array', + // ); + expect(sandwiched).toEqual({ foo: ['bar', { first: '123', second: '456' }, 'baz'] }); + + var nestedArrays = merge({ foo: ['baz'] }, { foo: ['bar', 'xyzzy'] }); + // t.deepEqual(nestedArrays, { foo: ['baz', 'bar', 'xyzzy'] }); + expect(nestedArrays).toEqual({ foo: ['baz', 'bar', 'xyzzy'] }); + + var noOptionsNonObjectSource = merge({ foo: 'baz' }, 'bar'); + // t.deepEqual(noOptionsNonObjectSource, { foo: 'baz', bar: true }); + expect(noOptionsNonObjectSource).toEqual({ foo: 'baz', bar: true }); + + (typeof Object.defineProperty !== 'function' ? test.skip : test)( + 'avoids invoking array setters unnecessarily', + function () { + var setCount = 0; + var getCount = 0; + var observed: any[] = []; + Object.defineProperty(observed, 0, { + get: function () { + getCount += 1; + return { bar: 'baz' }; + }, + set: function () { + setCount += 1; + }, + }); + merge(observed, [null]); + // st.equal(setCount, 0); + // st.equal(getCount, 1); + expect(setCount).toEqual(0); + expect(getCount).toEqual(1); + observed[0] = observed[0]; // eslint-disable-line no-self-assign + // st.equal(setCount, 1); + // st.equal(getCount, 2); + expect(setCount).toEqual(1); + expect(getCount).toEqual(2); + }, + ); +}); + +test('assign()', function () { + var target = { a: 1, b: 2 }; + var source = { b: 3, c: 4 }; + var result = assign_single_source(target, source); + + expect(result).toEqual(target); + expect(target).toEqual({ a: 1, b: 3, c: 4 }); + expect(source).toEqual({ b: 3, c: 4 }); +}); + +describe('combine()', function () { + test('both arrays', function () { + var a = [1]; + var b = [2]; + var combined = combine(a, b); + + // st.deepEqual(a, [1], 'a is not mutated'); + // st.deepEqual(b, [2], 'b is not mutated'); + // st.notEqual(a, combined, 'a !== combined'); + // st.notEqual(b, combined, 'b !== combined'); + // st.deepEqual(combined, [1, 2], 'combined is a + b'); + expect(a).toEqual([1]); + expect(b).toEqual([2]); + expect(combined).toEqual([1, 2]); + expect(a).not.toEqual(combined); + expect(b).not.toEqual(combined); + }); + + test('one array, one non-array', function () { + var aN = 1; + var a = [aN]; + var bN = 2; + var b = [bN]; + + var combinedAnB = combine(aN, b); + // st.deepEqual(b, [bN], 'b is not mutated'); + // st.notEqual(aN, combinedAnB, 'aN + b !== aN'); + // st.notEqual(a, combinedAnB, 'aN + b !== a'); + // st.notEqual(bN, combinedAnB, 'aN + b !== bN'); + // st.notEqual(b, combinedAnB, 'aN + b !== b'); + // st.deepEqual([1, 2], combinedAnB, 'first argument is array-wrapped when not an array'); + expect(b).toEqual([bN]); + expect(combinedAnB).not.toEqual(aN); + expect(combinedAnB).not.toEqual(a); + expect(combinedAnB).not.toEqual(bN); + expect(combinedAnB).not.toEqual(b); + expect(combinedAnB).toEqual([1, 2]); + + var combinedABn = combine(a, bN); + // st.deepEqual(a, [aN], 'a is not mutated'); + // st.notEqual(aN, combinedABn, 'a + bN !== aN'); + // st.notEqual(a, combinedABn, 'a + bN !== a'); + // st.notEqual(bN, combinedABn, 'a + bN !== bN'); + // st.notEqual(b, combinedABn, 'a + bN !== b'); + // st.deepEqual([1, 2], combinedABn, 'second argument is array-wrapped when not an array'); + expect(a).toEqual([aN]); + expect(combinedABn).not.toEqual(aN); + expect(combinedABn).not.toEqual(a); + expect(combinedABn).not.toEqual(bN); + expect(combinedABn).not.toEqual(b); + expect(combinedABn).toEqual([1, 2]); + }); + + test('neither is an array', function () { + var combined = combine(1, 2); + // st.notEqual(1, combined, '1 + 2 !== 1'); + // st.notEqual(2, combined, '1 + 2 !== 2'); + // st.deepEqual([1, 2], combined, 'both arguments are array-wrapped when not an array'); + expect(combined).not.toEqual(1); + expect(combined).not.toEqual(2); + expect(combined).toEqual([1, 2]); + }); +}); + +test('is_buffer()', function () { + for (const x of [null, undefined, true, false, '', 'abc', 42, 0, NaN, {}, [], function () {}, /a/g]) { + // t.equal(is_buffer(x), false, inspect(x) + ' is not a buffer'); + expect(is_buffer(x)).toEqual(false); + } + + var fakeBuffer = { constructor: Buffer }; + // t.equal(is_buffer(fakeBuffer), false, 'fake buffer is not a buffer'); + expect(is_buffer(fakeBuffer)).toEqual(false); + + var saferBuffer = Buffer.from('abc'); + // t.equal(is_buffer(saferBuffer), true, 'SaferBuffer instance is a buffer'); + expect(is_buffer(saferBuffer)).toEqual(true); + + var buffer = Buffer.from('abc'); + // t.equal(is_buffer(buffer), true, 'real Buffer instance is a buffer'); + expect(is_buffer(buffer)).toEqual(true); +}); diff --git a/tests/responses.test.ts b/tests/responses.test.ts index ef6ba27bf..fbd073a79 100644 --- a/tests/responses.test.ts +++ b/tests/responses.test.ts @@ -1,5 +1,8 @@ -import { createResponseHeaders } from 'openai/core'; +import { APIPromise, createResponseHeaders } from 'openai/core'; +import OpenAI from 'openai/index'; import { Headers } from 'openai/_shims/index'; +import { Response } from 'node-fetch'; +import { compareType } from './utils/typing'; describe('response parsing', () => { // TODO: test unicode characters @@ -23,3 +26,102 @@ describe('response parsing', () => { expect(headers['content-type']).toBe('text/xml, application/json'); }); }); + +describe('request id', () => { + test('types', () => { + compareType>, string>(true); + compareType>, number>(true); + compareType>, null>(true); + compareType>, void>(true); + compareType>, Response>(true); + compareType>, Response>(true); + compareType>, { foo: string } & { _request_id?: string | null }>( + true, + ); + compareType>>, Array<{ foo: string }>>(true); + }); + + test('object response', async () => { + const client = new OpenAI({ + apiKey: 'dummy', + fetch: async () => + new Response(JSON.stringify({ id: 'bar' }), { + headers: { 'x-request-id': 'req_id_xxx', 'content-type': 'application/json' }, + }), + }); + + const rsp = await client.chat.completions.create({ messages: [], model: 'gpt-4' }); + expect(rsp.id).toBe('bar'); + expect(rsp._request_id).toBe('req_id_xxx'); + expect(JSON.stringify(rsp)).toBe('{"id":"bar"}'); + }); + + test('envelope response', async () => { + const promise = new APIPromise<{ data: { foo: string } }>( + (async () => { + return { + response: new Response(JSON.stringify({ data: { foo: 'bar' } }), { + headers: { 'x-request-id': 'req_id_xxx', 'content-type': 'application/json' }, + }), + controller: {} as any, + options: {} as any, + }; + })(), + )._thenUnwrap((d) => d.data); + + const rsp = await promise; + expect(rsp.foo).toBe('bar'); + expect(rsp._request_id).toBe('req_id_xxx'); + }); + + test('page response', async () => { + const client = new OpenAI({ + apiKey: 'dummy', + fetch: async () => + new Response(JSON.stringify({ data: [{ foo: 'bar' }] }), { + headers: { 'x-request-id': 'req_id_xxx', 'content-type': 'application/json' }, + }), + }); + + const page = await client.fineTuning.jobs.list(); + expect(page.data).toMatchObject([{ foo: 'bar' }]); + expect((page as any)._request_id).toBeUndefined(); + }); + + test('array response', async () => { + const promise = new APIPromise>( + (async () => { + return { + response: new Response(JSON.stringify([{ foo: 'bar' }]), { + headers: { 'x-request-id': 'req_id_xxx', 'content-type': 'application/json' }, + }), + controller: {} as any, + options: {} as any, + }; + })(), + ); + + const rsp = await promise; + expect(rsp.length).toBe(1); + expect(rsp[0]).toMatchObject({ foo: 'bar' }); + expect((rsp as any)._request_id).toBeUndefined(); + }); + + test('string response', async () => { + const promise = new APIPromise( + (async () => { + return { + response: new Response('hello world', { + headers: { 'x-request-id': 'req_id_xxx', 'content-type': 'application/text' }, + }), + controller: {} as any, + options: {} as any, + }; + })(), + ); + + const result = await promise; + expect(result).toBe('hello world'); + expect((result as any)._request_id).toBeUndefined(); + }); +}); diff --git a/tests/utils/typing.ts b/tests/utils/typing.ts new file mode 100644 index 000000000..4a791d2a7 --- /dev/null +++ b/tests/utils/typing.ts @@ -0,0 +1,9 @@ +type Equal = (() => T extends X ? 1 : 2) extends () => T extends Y ? 1 : 2 ? true : false; + +export const expectType = (_expression: T): void => { + return; +}; + +export const compareType = (_expression: Equal): void => { + return; +}; diff --git a/yarn.lock b/yarn.lock index 68486892b..5a01e39e3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -881,11 +881,6 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.18.tgz#8dfb97f0da23c2293e554c5a50d61ef134d7697f" integrity sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA== -"@types/qs@^6.9.15": - version "6.9.15" - resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.15.tgz#adde8a060ec9c305a82de1babc1056e73bd64dce" - integrity sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg== - "@types/semver@^7.5.0": version "7.5.3" resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.3.tgz#9a726e116beb26c24f1ccd6850201e1246122e04" @@ -1205,12 +1200,12 @@ brace-expansion@^2.0.1: dependencies: balanced-match "^1.0.0" -braces@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" - integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== +braces@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== dependencies: - fill-range "^7.0.1" + fill-range "^7.1.1" browserslist@^4.22.2: version "4.22.2" @@ -1248,17 +1243,6 @@ bundle-name@^3.0.0: dependencies: run-applescript "^5.0.0" -call-bind@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" - integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== - dependencies: - es-define-property "^1.0.0" - es-errors "^1.3.0" - function-bind "^1.1.2" - get-intrinsic "^1.2.4" - set-function-length "^1.2.1" - callsites@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" @@ -1448,15 +1432,6 @@ default-browser@^4.0.0: execa "^7.1.1" titleize "^3.0.0" -define-data-property@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" - integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== - dependencies: - es-define-property "^1.0.0" - es-errors "^1.3.0" - gopd "^1.0.1" - define-lazy-prop@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz#dbb19adfb746d7fc6d734a06b72f4a00d021255f" @@ -1523,18 +1498,6 @@ error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -es-define-property@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" - integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== - dependencies: - get-intrinsic "^1.2.4" - -es-errors@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" - integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== - escalade@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" @@ -1806,10 +1769,10 @@ file-entry-cache@^6.0.1: dependencies: flat-cache "^3.0.4" -fill-range@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" - integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== dependencies: to-regex-range "^5.0.1" @@ -1889,17 +1852,6 @@ get-caller-file@^2.0.5: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-intrinsic@^1.1.3, get-intrinsic@^1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" - integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== - dependencies: - es-errors "^1.3.0" - function-bind "^1.1.2" - has-proto "^1.0.1" - has-symbols "^1.0.3" - hasown "^2.0.0" - get-package-type@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" @@ -1965,13 +1917,6 @@ globby@^11.1.0: merge2 "^1.4.1" slash "^3.0.0" -gopd@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" - integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== - dependencies: - get-intrinsic "^1.1.3" - graceful-fs@^4.2.9: version "4.2.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" @@ -1992,23 +1937,6 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== -has-property-descriptors@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" - integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== - dependencies: - es-define-property "^1.0.0" - -has-proto@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.3.tgz#b31ddfe9b0e6e9914536a6ab286426d0214f77fd" - integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q== - -has-symbols@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" - integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== - hasown@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.0.tgz#f4c513d454a57b7c7e1650778de226b11700546c" @@ -2038,6 +1966,13 @@ humanize-ms@^1.2.1: dependencies: ms "^2.0.0" +iconv-lite@^0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" + integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + ignore@^5.2.0, ignore@^5.2.4: version "5.2.4" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" @@ -2717,11 +2652,11 @@ merge2@^1.3.0, merge2@^1.4.1: integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== micromatch@^4.0.4: - version "4.0.5" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" - integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== + version "4.0.8" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" + integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== dependencies: - braces "^3.0.2" + braces "^3.0.3" picomatch "^2.3.1" mime-db@1.51.0: @@ -2826,11 +2761,6 @@ npm-run-path@^5.1.0: dependencies: path-key "^4.0.0" -object-inspect@^1.13.1: - version "1.13.2" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.2.tgz#dea0088467fb991e67af4058147a24824a3043ff" - integrity sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g== - once@^1.3.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -3049,13 +2979,6 @@ pure-rand@^6.1.0: resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.1.0.tgz#d173cf23258231976ccbdb05247c9787957604f2" integrity sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA== -qs@^6.10.3: - version "6.13.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906" - integrity sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg== - dependencies: - side-channel "^1.0.6" - queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" @@ -3142,6 +3065,11 @@ safe-buffer@~5.2.0: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== +"safer-buffer@>= 2.1.2 < 3.0.0": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + semver@^6.3.0, semver@^6.3.1: version "6.3.1" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" @@ -3154,18 +3082,6 @@ semver@^7.5.3, semver@^7.5.4: dependencies: lru-cache "^6.0.0" -set-function-length@^1.2.1: - version "1.2.2" - resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" - integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== - dependencies: - define-data-property "^1.1.4" - es-errors "^1.3.0" - function-bind "^1.1.2" - get-intrinsic "^1.2.4" - gopd "^1.0.1" - has-property-descriptors "^1.0.2" - shebang-command@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" @@ -3178,16 +3094,6 @@ shebang-regex@^3.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== -side-channel@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2" - integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA== - dependencies: - call-bind "^1.0.7" - es-errors "^1.3.0" - get-intrinsic "^1.2.4" - object-inspect "^1.13.1" - signal-exit@^3.0.3, signal-exit@^3.0.7: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9"